commit 02061d74c23d7f1ad5ea0bd1cc0c52dfe5ed2758 Author: maride Date: Sat Apr 2 14:43:55 2016 +0200 Original 1.0.5 code (as received from Jonas Echterhoff) diff --git a/English.lproj/InfoPlist.strings b/English.lproj/InfoPlist.strings new file mode 100644 index 0000000..4a342df Binary files /dev/null and b/English.lproj/InfoPlist.strings differ diff --git a/HID_cookie_strings.plist b/HID_cookie_strings.plist new file mode 100755 index 0000000..927779d --- /dev/null +++ b/HID_cookie_strings.plist @@ -0,0 +1,245 @@ + + + + + 1035 + + 25907 + + 3 + Left Button + 4 + Right Button + 5 + Small Left Button + 6 + Small Right Button + 7 + X-Axis + 8 + Y-Axis + Name + Competition Pro + + Name + MOSIC + + 1118 + + 27 + + 27 + Button 1 [Trigger] + 28 + Button 2 + 29 + Button 3 + 30 + Button 4 + 31 + Button 5 + 32 + Button 6 + 33 + Button 7 + 34 + Button 8 + 89 + X-Axis + 90 + Y-Axis + 91 + Rz-Axis + 92 + Throttle + 93 + Hat Switch + Name + SideWinder FFB 2 Joystick + + Name + Microsoft + + 1133 + + 49797 + + 10 + Button 6 + 11 + Button 7 + 16 + Hat Switch 2 + 24 + X-Axis + 25 + Y-Axis + 26 + Hat Switch 1 + 27 + Rz-Axis + 28 + Throttle + 29 + Button 9 + 30 + Button 8 + 5 + Button 1 [Trigger] + 6 + Button 2 + 7 + Button 3 + 8 + Button 4 + 9 + Button 5 + Name + WingMan Strike Force 3D + + Name + Logitech + + 1635 + + 38916 + + 10 + R2 Trigger + 11 + Right Stick X-Axis + 12 + Right Stick Y-Axis + 13 + Left Stick X-Axis + 14 + Left Stick Y-Axis + 15 + Hat Switch + 3 + Button 1 + 4 + Button 2 + 5 + Button 3 + 6 + Button 4 + 7 + L1 Trigger + 8 + R1 Trigger + 9 + L2 Trigger + Name + FunPad F-107 + + Name + Macsense + + 8738 + + 16400 + + 10 + Left Button + 11 + C Button + 12 + B Button [Select] + 13 + A Button [Start] + 14 + F Button + 15 + R1 Trigger + 16 + R2 Trigger + 17 + L1 Trigger + 18 + L2 Trigger + 19 + Left Stick Button + 20 + Right Stick Button + 21 + D Button + 22 + E Button + 23 + Left Stick X-Axis + 24 + Left Stick Y-Axis + 25 + Right Stick X-Axis + 26 + Right Stick Y-Axis + 3 + D-Pad Up + 4 + D-Pad Down + 5 + D-Pad Left + 6 + D-Pad Right + 7 + Up Button + 8 + Right Button + 9 + Down Button + Name + iShock + + 16416 + + 10 + D Button + 11 + Button 1 + 12 + Button 2 [Select] + 13 + Button 3 [Start] + 14 + R1 Button + 15 + R2 Trigger + 16 + L1 Trigger + 17 + L2 Trigger + 18 + Left Stick Button + 19 + Right Stick Button + 20 + Left Stick X-Axis + 21 + Left Stick Y-Axis + 22 + Right Stick X-Axis + 23 + Right Stick Y-Axis + 3 + D-Pad Up + 4 + D-Pad Down + 5 + D-Pad Left + 6 + D-Pad Right + 7 + A Button + 8 + B Button + 9 + C Button + Name + iShock II + + Name + Macally + + + diff --git a/HID_device_usage_strings.plist b/HID_device_usage_strings.plist new file mode 100755 index 0000000..886c86a --- /dev/null +++ b/HID_device_usage_strings.plist @@ -0,0 +1,633 @@ + + + + + 1118 + + 26 + + 1:48 + Wheel + 1:49 + Left Pedal [Brake] + 1:50 + Right Pedal [Gas] + 9:1 + Button A + 9:2 + Button B + 9:3 + Button C + 9:4 + Button X + 9:5 + Button Y + 9:6 + Button Z + 9:7 + Left Trigger + 9:8 + Right Trigger + Name + SideWinder Precision Racing Wheel USB v1.0 + + 27 + + 1:48 + X-Axis + 1:49 + Y-Axis + 1:53 + Rz-Axis + 1:54 + Throttle + 1:57 + Hat Switch + 9:1 + Button 1 [Trigger] + 9:2 + Button 2 + 9:3 + Button 3 + 9:4 + Button 4 + 9:5 + Button 5 + 9:6 + Button 6 + 9:7 + Button 7 + 9:8 + Button 8 + Name + SideWinder FFB 2 Joystick + + 39 + + 1:48 + X-Axis + 1:49 + Y-Axis + 9:1 + Button 1 + 9:2 + Button 2 + 9:3 + Button 3 + 9:4 + Button 4 + 9:5 + Button 5 + 9:6 + Button 6 + Name + SideWinder Plug and Play Game Pad + + 56 + + 1:48 + X-Axis + 1:49 + Y-Axis + 1:53 + Rz-Axis + 1:54 + Throttle + 1:57 + Hat Switch + 9:1 + Button 1 [Trigger] + 9:2 + Button 2 + 9:3 + Button 3 + 9:4 + Button 4 + 9:5 + Button 5 + 9:6 + Button 6 + 9:7 + Button 7 + 9:8 + Button 8 + Name + SideWinder Precision 2 Joystick + + 60 + + 1:48 + X-Axis + 1:49 + Y-Axis + 1:54 + Throttle + 9:1 + Button 1 [Trigger] + 9:2 + Button 2 + 9:3 + Button 3 + 9:4 + Button 4 + 9:5 + Button 5 + 9:6 + Button 6 + 9:7 + Button 7 + 9:8 + Button 8 + Name + SideWinder Joystick + + 7 + + 1:48 + X-Axis + 1:49 + Y-Axis + 9:1 + Button A + 9:10 + Secondary Option 1 + 9:2 + Button B + 9:3 + Button C + 9:4 + Button X + 9:5 + Button Y + 9:6 + Button Z + 9:7 + Left Trigger + 9:8 + Right Trigger + 9:9 + Secondary Option 2 + Name + SideWinder Game Pad USB + + Name + Microsoft + + 1133 + + 49200 + + 1:1 + Pointer + 1:2 + Mouse + 1:48 + X-Axis + 1:49 + Y-Axis + 1:56 + Wheel + 9:1 + Left Button + 9:2 + Right Button + 9:3 + Middle Button + Name + iFeel Mouse + + 49671 + + 1:48 + X-Axis + 1:49 + Y-Axis + 1:53 + Rz-Axis + 1:54 + Throttle + 1:57 + Hat Switch + 9:1 + Button 1 [Trigger] + 9:2 + Button 2 + 9:3 + Button 3 + 9:4 + Button 4 + 9:5 + Button 5 + 9:6 + Button 6 + 9:7 + Button 7 + Name + WingMan Extreme Digital 3D + + 49797 + + 1:48 + X-Axis + 1:49 + Y-Axis + 1:53 + Rz-Axis + 1:54 + Throttle + 1:57 + Hat Switch 1 + 65280:2 + Thumb Wheel + 9:1 + Button 1 [Trigger] + 9:10 + Hat Switch 2 - Up + 9:11 + Hat Switch 2 - Right + 9:12 + Hat Switch 2 - Down + 9:13 + Hat Switch 2 - Left + 9:14 + Hat Switch 2 - Up Right + 9:15 + Hat Switch 2 - Down Right + 9:16 + Hat Switch 2 - Down Left + 9:17 + Hat Switch 2 - Up Left + 9:2 + Button 2 + 9:3 + Button 3 + 9:4 + Button 4 + 9:5 + Button 5 + 9:6 + Button 6 + 9:7 + Button 7 + 9:8 + Button 8 + 9:9 + Button 9 + Name + WingMan Strike Force 3D + + 49811 + + 1:48 + Wheel + 1:49 + Pedals + 65280:1 + Left Pedal [Brake] + 9:1 + Button 1 + 9:2 + Button 2 + 9:3 + Button 3 + 9:4 + Button 4 + 9:5 + Button 5 + 9:6 + Button 6 + Name + WingMan Formula Force GP + + 50433 + + 1:48 + X-Axis + 1:49 + Y-Axis + 1:56 + Wheel + 9:1 + Left Button + 9:2 + Right Button + 9:3 + Middle Button + Name + Cordless Mouse + + Name + Logitech + + 1149 + + 12293 + + 1:48 + X-Axis + 1:49 + Y-Axis + 1:50 + Throttle + 1:57 + Hat Switch + 9:1 + Button 1 [Trigger] + 9:10 + Button 10 [Thumb Wheel Right] + 9:2 + Button 2 + 9:3 + Button 3 + 9:4 + Button 4 + 9:5 + Button 5 + 9:6 + Button 6 [Thumb Wheel Button] + 9:7 + Button 7 + 9:8 + Button 8 + 9:9 + Button 9 [Thumb Wheel Left] + Name + Eliminator Precision Pro Joystick + + Name + Gravis + + 1293 + + 2051 + + 1:48 + Left Stick X-Axis + 1:49 + Left Stick Y-Axis + 1:50 + Right Stick Y-Axis + 1:53 + Right Stick X-Axis + 1:57 + Direction Pad + 9:1 + Button 1 + 9:10 + Mouse + 9:11 + Eater + 9:12 + Right Stick Button + 9:13 + Left Stick Button + 9:2 + Button 2 + 9:3 + Button 3 + 9:4 + Button 4 + 9:5 + Left Top Trigger + 9:6 + Left Bottom Trigger + 9:7 + Right Top Trigger + 9:8 + Right Bottom Trigger + 9:9 + ESC + Name + Nostromo n45 + + Name + Belkin + + 1452 + + 516 + + 1:6 + Keyboard + Name + Apple Extended USB Keyboard + + 770 + + 1:1 + Pointer + 1:2 + Mouse + 1:48 + X-Axis + 1:49 + Y-Axis + 9:1 + Button + Name + Apple Optical USB Mouse + + Name + Mitsumi Electric + + 1635 + + 38916 + + 1:48 + Left Stick X-Axis + 1:49 + Left Stick Y-Axis + 1:57 + Hat Switch + 2:186 + Right Stick X-Axis + 2:187 + Right Stick Y-Axis + 9:1 + Button 1 + 9:2 + Button 2 + 9:3 + Button 3 + 9:4 + Button 4 + 9:5 + L1 Trigger + 9:6 + R1 Trigger + 9:7 + L2 Trigger + 9:8 + R2 Trigger + Name + FunPad F-107 + + Name + Macsense + + 1699 + + 65284 + + 1:48 + Wheel + 1:49 + Left Pedal [Brake] + 1:50 + Right Pedal [Gas] + 9:1 + Top Left Thumb + 9:2 + Top Right Thumb + 9:3 + Bottom Left Thumb + 9:4 + Bottom Right Thumb + 9:5 + Right Horn + 9:6 + Left Horn + Name + R440 Force Feedback + + Name + Saitek + + 1973 + + 39169 + + 1:48 + X-Axis + 1:49 + Y-Axis + 1:54 + Throttle + 1:57 + Hat Switch + 9:1 + Button 1 [Trigger] + 9:2 + Button 2 + 9:3 + Button 3 + 9:4 + Button 4 + Name + X8-33GU 2 IN 1 Joystick + + Name + Saitek + + 8738 + + 16400 + + 1:48 + Left Stick X-Axis + 1:49 + Left Stick Y-Axis + 1:53 + Right Stick X-Axis + 1:54 + Right Stick Y-Axis + 9:1 + D-Pad Up + 9:10 + Button B [Select] + 9:11 + Button A [Start] + 9:12 + Button F + 9:13 + R1 Trigger + 9:14 + R2 Trigger + 9:15 + L1 Trigger + 9:16 + L2 Trigger + 9:17 + Left Stick Button + 9:18 + Right Stick Button + 9:19 + D Button + 9:2 + D-Pad Down + 9:20 + E Button + 9:3 + D-Pad Left + 9:4 + D-Pad Right + 9:5 + Button 5 (Triangle) + 9:6 + Button 6 (Circle) + 9:7 + Button 7 (Cross) + 9:8 + Button 8 (Square) + 9:9 + Button C + Name + iShock + + 16416 + + 1:1 + Pointer + 1:48 + Left Stick X-Axis + 1:49 + Left Stick Y-Axis + 1:5 + GamePad + 1:53 + Right Stick X-Axis + 1:54 + Right Stick Y-Axis + 9:1 + D-Pad Up + 9:10 + Button 2 [Select] + 9:11 + Button 3 [Start] + 9:12 + R1 Button + 9:13 + R2 Trigger + 9:14 + L1 Trigger + 9:15 + L2 Trigger + 9:16 + Left Stick Button + 9:17 + Right Stick Button + 9:2 + D-Pad Down + 9:3 + D-Pad Left + 9:4 + D-Pad Right + 9:5 + A Button + 9:6 + B Button + 9:7 + C Button + 9:8 + D Button + 9:9 + Button 1 + Name + iShock II + + Name + Macally + + + diff --git a/HID_usage_strings.plist b/HID_usage_strings.plist new file mode 100755 index 0000000..cd068bf --- /dev/null +++ b/HID_usage_strings.plist @@ -0,0 +1,200 @@ + + + + + 0x0001 + + Name Generic Desktop + 0x0001 Pointer + 0x0002 Mouse + 0x0004 Joystick + 0x0005 GamePad + 0x0006 Keyboard + 0x0007 Keypad + 0x0008 MultiAxisController + 0x0030 X + 0x0031 Y + 0x0032 Z + 0x0033 Rx + 0x0034 Ry + 0x0035 Rz + 0x0036 Slider + 0x0037 Dial + 0x0038 Wheel + 0x0039 Hatswitch + 0x003A Counted Buffer + 0x003B Byte Count + 0x003C Motion Wakeup + 0x003D Start + 0x003E Select + 0x0040 Vx + 0x0041 Vy + 0x0042 Vz + 0x0043 Vbrx + 0x0044 Vbry + 0x0045 Vbrz + 0x0046 Vno + 0x0080 System Control + 0x0081 System Power Down + 0x0082 System Sleep + 0x0083 System Wake Up + 0x0084 SystemContext Menu + 0x0085 System Main Menu + 0x0086 System App Menu + 0x0087 System Menu help + 0x0088 System Menu Exit + 0x0089 System Menu + 0x008A System Menu Right + 0x008B System Menu Left + 0x008C System Menu Up + 0x008D System Menu Down + 0x0090 DPad Up + 0x0091 DPad Down + 0x0092 DPad Right + 0x0093 DPad Left + + 0x0002 + + Name Simulation + 0x0001 Flight Simulation Device + 0x0002 Automobile Simulation Device + 0x0003 Tank Simulation Device + 0x0004 Spaceship Simulation Device + 0x0005 Submarine Simulation Device + 0x0006 Sailing Simulation Device + 0x0007 Motorcycle Simulation Device + 0x0008 Sports Simulation Device + 0x0009 Airplane Simulation Device + 0x000A Helicopter Simulation Device + 0x000B Magic Carpet Simulation Device + 0x000C Bicycle Simulation Device + 0x0020 Flight Control Stick + 0x0021 Flight Stick + 0x0022 Cyclic Control + 0x0023 Cyclic Trim + 0x0024 Flight Yoke + 0x0025 Track Control + 0x00B0 Aileron + 0x00B1 Aileron Trim + 0x00B2 Anti Torque Control + 0x00B5 Collective Control + 0x00B6 Dive Brake + 0x00B7 Electronic Countermeasures + 0x00B8 Elevator + 0x00B9 Elevator Trim + 0x00BA Rudder + 0x00BB Throttle + 0x00BC Flight Communications + 0x00BD Flare Release + 0x00BE Landing Gear + 0x00BF Toe Brake + 0x00C0 Trigger + 0x00C1 Weapons Arm + 0x00C2 Weapons + 0x00C3 Wing Flaps + 0x00C4 Accelerator + 0x00C5 Brake + 0x00C6 Clutch + 0x00C7 Shifter + 0x00C8 Steering + 0x00C9 Turret Direction + 0x00CA Barrel Elevation + 0x00CB Dive Plane + 0x00CC Ballast + 0x00CD Bicycle Crank + 0x00CE Handle Bars + 0x00CF Front Brake + 0x00D0 Rear Brake + + 0x0003 + + Name Virtual Reality + 0x0001 Belt + 0x0002 Body Suit + 0x0003 Flexor + 0x0004 Glove + 0x0005 Head Tracker + 0x0006 Head Mounted Display + 0x0007 Hand Tracker + 0x0008 Oculometer + 0x0009 Vest + 0x000A Animatronic Device + 0x0020 Stereo Enable + 0x0021 Display Enable + + 0x0004 + + Name Sport + 0x0001 Baseball Bat + 0x0002 Golf Club + 0x0003 Rowing Machine + 0x0004 Treadmill + + 0x0030 Oar + 0x0031 Slope + 0x0032 Rate + 0x0033 Stick Speed + 0x0034 Stick Face Angle + 0x0035 Stick Heel Or Toe + 0x0036 Stick Follow Through + 0x0037 Stick Tempo + 0x0038 Stick Type + 0x0039 Stick Height + + 0x0050 Putter + 0x0051 1 Iron + 0x0052 2 Iron + 0x0053 3 Iron + 0x0054 4 Iron + 0x0055 5 Iron + 0x0056 6 Iron + 0x0057 7 Iron + 0x0058 8 Iron + 0x0059 9 Iron + 0x005A 10 Iron + 0x005B 11 Iron + 0x005C Sand Wedge + 0x005D Loft Wedge + 0x005E Power Wedge + 0x005F 1 Wood + 0x0060 3 Wood + 0x0061 5 Wood + 0x0062 7 Wood + 0x0063 9 Wood + + 0x0005 + + Name Game + 0x0001 3D Game Controller + 0x0002 Pinball Device + 0x0003 Gun + + 0x0020 Point of View + 0x0021 Turn Right Or Left + 0x0022 Pitch Up Or Down + 0x0023 Roll Right Or Left + 0x0024 Move Right Or Left + 0x0025 Move Forward Or Backward + 0x0026 Move Up Or Down + 0x0027 Lean Right Or Left + 0x0029 Lean Forward Or Backward + 0x0029 Height Of POV + 0x002A Flipper + 0x002B Secondary Flipper + 0x002C Bump + 0x002D New Game + 0x002E Shoot Ball + 0x002F Player + + 0x0030 Gun Bolt + 0x0031 Gun Clip + 0x0032 Gun + 0x0033 Gun Single Shot + 0x0034 Gun Burst + 0x0035 Gun Automatic + 0x0036 Gun Safety + 0x0037 Gamepad Fire Or Jump + 0x0039 Gamepad Trigger + + + diff --git a/Info.plist b/Info.plist new file mode 100644 index 0000000..0cc09cc --- /dev/null +++ b/Info.plist @@ -0,0 +1,293 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleDocumentTypes + + + CFBundleOSTypes + + PACK + + CFBundleTypeExtensions + + redplug + + CFBundleTypeIconFile + Plugin.icns + CFBundleTypeName + Redline Plugin + CFBundleTypeRole + Viewer + LSIsAppleDefaultForType + + LSItemContentTypes + + com.ambrosiasw.redline.plugin + + LSTypeIsPackage + + + + CFBundleExecutable + Redline + CFBundleGetInfoString + Redline version 1.0.5, ©2003-2008 Ambrosia Software + CFBundleIconFile + Redline + CFBundleIdentifier + com.ambrosiasw.redline + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleShortVersionString + Redline 1.0.5 + CFBundleSignature + GLrc + CFBundleURLTypes + + + CFBundleURLName + redline URL + CFBundleURLSchemes + + redline + + + + CFBundleVersion + 1.0.5 + CSResourcesFileMapped + + UTExportedTypeDeclarations + + + UTTypeConformsTo + + com.ambrosiasw.redline.config-file + + UTTypeDescription + Redline Car Document + UTTypeIdentifier + com.ambrosiasw.redline.car + UTTypeTagSpecification + + public.filename-extension + + car + + + + + UTTypeConformsTo + + public.text + public.content + + UTTypeDescription + Redline Configuration File + UTTypeIdentifier + com.ambrosiasw.redline.config-file + + + UTTypeConformsTo + + com.ambrosiasw.redline.config-file + + UTTypeDescription + Redline Entity Document + UTTypeIdentifier + com.ambrosiasw.redline.entity + UTTypeTagSpecification + + public.filename-extension + + ent + + + + + UTTypeConformsTo + + com.ambrosiasw.redline.config-file + + UTTypeDescription + Redline Environment Document + UTTypeIdentifier + com.ambrosiasw.redline.environment + UTTypeTagSpecification + + public.filename-extension + + env + + + + + UTTypeConformsTo + + com.ambrosiasw.redline.config-file + + UTTypeDescription + Redline Font Document + UTTypeIdentifier + com.ambrosiasw.redline.font + UTTypeTagSpecification + + public.filename-extension + + font + + + + + UTTypeConformsTo + + com.ambrosiasw.redline.config-file + + UTTypeDescription + Redline Map Description Document + UTTypeIdentifier + com.ambrosiasw.redline.mapinfo + UTTypeTagSpecification + + public.filename-extension + + mapinfo + + + + + UTTypeConformsTo + + public.data + public.content + + UTTypeDescription + Redline 3D Model Document + UTTypeIdentifier + com.ambrosiasw.redline.model + UTTypeTagSpecification + + public.filename-extension + + mdl + + + + + UTTypeConformsTo + + public.data + public.content + + UTTypeDescription + Redline Plug-in + UTTypeIdentifier + com.ambrosiasw.redline.plugin + UTTypeTagSpecification + + com.apple.ostype + + PACK + + public.filename-extension + + redplug + + + + + UTTypeConformsTo + + com.ambrosiasw.redline.config-file + + UTTypeDescription + Redline Preferences File + UTTypeIdentifier + com.ambrosiasw.redline.preferences + UTTypeTagSpecification + + public.filename-extension + + cfg + + + + + UTTypeConformsTo + + public.data + public.content + + UTTypeDescription + Redline Replay + UTTypeIdentifier + com.ambrosiasw.redline.replay + UTTypeTagSpecification + + public.filename-extension + + redlog + sredlog + + + + + UTTypeConformsTo + + public.data + public.content + + UTTypeDescription + Redline Road Data Document + UTTypeIdentifier + com.ambrosiasw.redline.road + UTTypeTagSpecification + + public.filename-extension + + road + + + + + UTTypeConformsTo + + com.ambrosiasw.redline.config-file + + UTTypeDescription + Redline Road Types Document + UTTypeIdentifier + com.ambrosiasw.redline.roadtypes + UTTypeTagSpecification + + public.filename-extension + + roadtypes + + + + + UTTypeConformsTo + + com.ambrosiasw.redline.config-file + + UTTypeDescription + Redline Road Surfaces Document + UTTypeIdentifier + com.ambrosiasw.redline.surfaces + UTTypeTagSpecification + + public.filename-extension + + surfaces + + + + + + diff --git a/Next Track.scpt b/Next Track.scpt new file mode 100644 index 0000000..116febd Binary files /dev/null and b/Next Track.scpt differ diff --git a/PlayPause.scpt b/PlayPause.scpt new file mode 100644 index 0000000..09abc96 Binary files /dev/null and b/PlayPause.scpt differ diff --git a/Plugin.icns b/Plugin.icns new file mode 100644 index 0000000..e31be02 Binary files /dev/null and b/Plugin.icns differ diff --git a/Prev Track.scpt b/Prev Track.scpt new file mode 100644 index 0000000..ff3657b Binary files /dev/null and b/Prev Track.scpt differ diff --git a/Redline.icns b/Redline.icns new file mode 100755 index 0000000..7015425 Binary files /dev/null and b/Redline.icns differ diff --git a/Redline.rsrc b/Redline.rsrc new file mode 100644 index 0000000..8a8b44b Binary files /dev/null and b/Redline.rsrc differ diff --git a/Status.scpt b/Status.scpt new file mode 100644 index 0000000..2bb8bb4 Binary files /dev/null and b/Status.scpt differ diff --git a/build/game.build/game.pbxindex/categories.pbxbtree b/build/game.build/game.pbxindex/categories.pbxbtree new file mode 100644 index 0000000..bc9fee3 Binary files /dev/null and b/build/game.build/game.pbxindex/categories.pbxbtree differ diff --git a/build/game.build/game.pbxindex/cdecls.pbxbtree b/build/game.build/game.pbxindex/cdecls.pbxbtree new file mode 100644 index 0000000..4e093bf Binary files /dev/null and b/build/game.build/game.pbxindex/cdecls.pbxbtree differ diff --git a/build/game.build/game.pbxindex/decls.pbxbtree b/build/game.build/game.pbxindex/decls.pbxbtree new file mode 100644 index 0000000..d1073c8 Binary files /dev/null and b/build/game.build/game.pbxindex/decls.pbxbtree differ diff --git a/build/game.build/game.pbxindex/files.pbxbtree b/build/game.build/game.pbxindex/files.pbxbtree new file mode 100644 index 0000000..c7aa9ec Binary files /dev/null and b/build/game.build/game.pbxindex/files.pbxbtree differ diff --git a/build/game.build/game.pbxindex/imports.pbxbtree b/build/game.build/game.pbxindex/imports.pbxbtree new file mode 100644 index 0000000..52c17ab Binary files /dev/null and b/build/game.build/game.pbxindex/imports.pbxbtree differ diff --git a/build/game.build/game.pbxindex/pbxindex.header b/build/game.build/game.pbxindex/pbxindex.header new file mode 100644 index 0000000..adc7e21 Binary files /dev/null and b/build/game.build/game.pbxindex/pbxindex.header differ diff --git a/build/game.build/game.pbxindex/protocols.pbxbtree b/build/game.build/game.pbxindex/protocols.pbxbtree new file mode 100644 index 0000000..df8b414 Binary files /dev/null and b/build/game.build/game.pbxindex/protocols.pbxbtree differ diff --git a/build/game.build/game.pbxindex/refs.pbxbtree b/build/game.build/game.pbxindex/refs.pbxbtree new file mode 100644 index 0000000..844bd9b Binary files /dev/null and b/build/game.build/game.pbxindex/refs.pbxbtree differ diff --git a/build/game.build/game.pbxindex/strings.pbxstrings/control b/build/game.build/game.pbxindex/strings.pbxstrings/control new file mode 100644 index 0000000..f47b727 Binary files /dev/null and b/build/game.build/game.pbxindex/strings.pbxstrings/control differ diff --git a/build/game.build/game.pbxindex/strings.pbxstrings/strings b/build/game.build/game.pbxindex/strings.pbxstrings/strings new file mode 100644 index 0000000..f992ffd Binary files /dev/null and b/build/game.build/game.pbxindex/strings.pbxstrings/strings differ diff --git a/build/game.build/game.pbxindex/subclasses.pbxbtree b/build/game.build/game.pbxindex/subclasses.pbxbtree new file mode 100644 index 0000000..c7cc9a2 Binary files /dev/null and b/build/game.build/game.pbxindex/subclasses.pbxbtree differ diff --git a/build/game.build/game.pbxindex/symbols0.pbxsymbols b/build/game.build/game.pbxindex/symbols0.pbxsymbols new file mode 100644 index 0000000..f4eb28e Binary files /dev/null and b/build/game.build/game.pbxindex/symbols0.pbxsymbols differ diff --git a/game.xcodeproj/jechter.mode1 b/game.xcodeproj/jechter.mode1 new file mode 100644 index 0000000..66ab3c0 --- /dev/null +++ b/game.xcodeproj/jechter.mode1 @@ -0,0 +1,1398 @@ + + + + + ActivePerspectiveName + Project + AllowedModules + + + BundleLoadPath + + MaxInstances + n + Module + PBXSmartGroupTreeModule + Name + Groups and Files Outline View + + + BundleLoadPath + + MaxInstances + n + Module + PBXNavigatorGroup + Name + Editor + + + BundleLoadPath + + MaxInstances + n + Module + XCTaskListModule + Name + Task List + + + BundleLoadPath + + MaxInstances + n + Module + XCDetailModule + Name + File and Smart Group Detail Viewer + + + BundleLoadPath + + MaxInstances + 1 + Module + PBXBuildResultsModule + Name + Detailed Build Results Viewer + + + BundleLoadPath + + MaxInstances + 1 + Module + PBXProjectFindModule + Name + Project Batch Find Tool + + + BundleLoadPath + + MaxInstances + n + Module + PBXRunSessionModule + Name + Run Log + + + BundleLoadPath + + MaxInstances + n + Module + PBXBookmarksModule + Name + Bookmarks Tool + + + BundleLoadPath + + MaxInstances + n + Module + PBXClassBrowserModule + Name + Class Browser + + + BundleLoadPath + + MaxInstances + n + Module + PBXCVSModule + Name + Source Code Control Tool + + + BundleLoadPath + + MaxInstances + n + Module + PBXDebugBreakpointsModule + Name + Debug Breakpoints Tool + + + BundleLoadPath + + MaxInstances + n + Module + XCDockableInspector + Name + Inspector + + + BundleLoadPath + + MaxInstances + n + Module + PBXOpenQuicklyModule + Name + Open Quickly Tool + + + BundleLoadPath + + MaxInstances + 1 + Module + PBXDebugSessionModule + Name + Debugger + + + BundleLoadPath + + MaxInstances + 1 + Module + PBXDebugCLIModule + Name + Debug Console + + + Description + DefaultDescriptionKey + DockingSystemVisible + + Extension + mode1 + FavBarConfig + + PBXProjectModuleGUID + 7F061371087703BC001EA95C + XCBarModuleItemNames + + XCBarModuleItems + + + FirstTimeWindowDisplayed + + Identifier + com.apple.perspectives.project.mode1 + MajorVersion + 31 + MinorVersion + 1 + Name + Default + Notifications + + OpenEditors + + PerspectiveWidths + + -1 + -1 + + Perspectives + + + ChosenToolbarItems + + active-target-popup + active-buildstyle-popup + NSToolbarFlexibleSpaceItem + buildOrClean + build-and-runOrDebug + com.apple.ide.PBXToolbarStopButton + get-info + toggle-editor + NSToolbarFlexibleSpaceItem + com.apple.pbx.toolbar.searchfield + + ControllerClassBaseName + + IconName + WindowOfProjectWithEditor + Identifier + perspective.project + IsVertical + + Layout + + + ContentConfiguration + + PBXBottomSmartGroupGIDs + + 1C37FBAC04509CD000000102 + 1C37FAAC04509CD000000102 + 1C08E77C0454961000C914BD + 1C37FABC05509CD000000102 + 1C37FABC05539CD112110102 + E2644B35053B69B200211256 + 1C37FABC04509CD000100104 + 1CC0EA4004350EF90044410B + 1CC0EA4004350EF90041110B + + PBXProjectModuleGUID + 1CE0B1FE06471DED0097A5F4 + PBXProjectModuleLabel + Files + PBXProjectStructureProvided + yes + PBXSmartGroupTreeModuleColumnData + + PBXSmartGroupTreeModuleColumnWidthsKey + + 282 + + PBXSmartGroupTreeModuleColumnsKey_v4 + + MainColumn + + + PBXSmartGroupTreeModuleOutlineStateKey_v7 + + PBXSmartGroupTreeModuleOutlineStateExpansionKey + + 20286C29FDCF999611CA2CEA + 20286C32FDCF999611CA2CEA + 1C37FBAC04509CD000000102 + 1C37FAAC04509CD000000102 + + PBXSmartGroupTreeModuleOutlineStateSelectionKey + + + 0 + + + PBXSmartGroupTreeModuleOutlineStateVisibleRectKey + {{0, 0}, {282, 598}} + + PBXTopSmartGroupGIDs + + XCIncludePerspectivesSwitch + + XCSharingToken + com.apple.Xcode.GFSharingToken + + GeometryConfiguration + + Frame + {{0, 0}, {299, 616}} + GroupTreeTableConfiguration + + MainColumn + 282 + + RubberWindowFrame + 465 134 636 657 0 0 1440 878 + + Module + PBXSmartGroupTreeModule + Proportion + 299pt + + + Dock + + + ContentConfiguration + + PBXProjectModuleGUID + 1CE0B20306471E060097A5F4 + PBXProjectModuleLabel + + PBXSplitModuleInNavigatorKey + + Split0 + + PBXProjectModuleGUID + 1CE0B20406471E060097A5F4 + PBXProjectModuleLabel + + history + + 7FEE16FB0B2891FE009EAF19 + 7F4AD4840B32BCBC00214BD8 + 7F4AD48B0B32C3BD00214BD8 + 7FEE690A0B4C3AB00066D0D8 + 7FEE692F0B4FFFCA0066D0D8 + 7FEE69300B4FFFCA0066D0D8 + 7FEE69310B4FFFCA0066D0D8 + 7FEE69320B4FFFCA0066D0D8 + 7FEE69330B4FFFCA0066D0D8 + 7FEE69340B4FFFCA0066D0D8 + 7FEE69360B4FFFCA0066D0D8 + + prevStack + + 7FEE16AB0B281810009EAF19 + 7FEE16DA0B283F08009EAF19 + 7FEE16FD0B2891FE009EAF19 + 7F4AD4860B32BCBC00214BD8 + 7F4AD48D0B32C3BD00214BD8 + 7FEE69390B4FFFCA0066D0D8 + 7FEE693A0B4FFFCA0066D0D8 + 7FEE693B0B4FFFCA0066D0D8 + 7FEE693C0B4FFFCA0066D0D8 + 7FEE693D0B4FFFCA0066D0D8 + 7FEE69400B4FFFCA0066D0D8 + + + SplitCount + 1 + + StatusBarVisibility + + + GeometryConfiguration + + Frame + {{0, 0}, {332, 0}} + RubberWindowFrame + 465 134 636 657 0 0 1440 878 + + Module + PBXNavigatorGroup + Proportion + 0pt + + + BecomeActive + + ContentConfiguration + + PBXProjectModuleGUID + 1CE0B20506471E060097A5F4 + PBXProjectModuleLabel + Detail + + GeometryConfiguration + + Frame + {{0, 5}, {332, 611}} + RubberWindowFrame + 465 134 636 657 0 0 1440 878 + + Module + XCDetailModule + Proportion + 611pt + + + Proportion + 332pt + + + Name + Project + ServiceClasses + + XCModuleDock + PBXSmartGroupTreeModule + XCModuleDock + PBXNavigatorGroup + XCDetailModule + + TableOfContents + + 7F6054220C1E94F400CDE511 + 1CE0B1FE06471DED0097A5F4 + 7F6054230C1E94F400CDE511 + 1CE0B20306471E060097A5F4 + 1CE0B20506471E060097A5F4 + + ToolbarConfiguration + xcode.toolbar.config.default + + + ControllerClassBaseName + + IconName + WindowOfProject + Identifier + perspective.morph + IsVertical + 0 + Layout + + + BecomeActive + 1 + ContentConfiguration + + PBXBottomSmartGroupGIDs + + 1C37FBAC04509CD000000102 + 1C37FAAC04509CD000000102 + 1C08E77C0454961000C914BD + 1C37FABC05509CD000000102 + 1C37FABC05539CD112110102 + E2644B35053B69B200211256 + 1C37FABC04509CD000100104 + 1CC0EA4004350EF90044410B + 1CC0EA4004350EF90041110B + + PBXProjectModuleGUID + 11E0B1FE06471DED0097A5F4 + PBXProjectModuleLabel + Files + PBXProjectStructureProvided + yes + PBXSmartGroupTreeModuleColumnData + + PBXSmartGroupTreeModuleColumnWidthsKey + + 186 + + PBXSmartGroupTreeModuleColumnsKey_v4 + + MainColumn + + + PBXSmartGroupTreeModuleOutlineStateKey_v7 + + PBXSmartGroupTreeModuleOutlineStateExpansionKey + + 29B97314FDCFA39411CA2CEA + 1C37FABC05509CD000000102 + + PBXSmartGroupTreeModuleOutlineStateSelectionKey + + + 0 + + + PBXSmartGroupTreeModuleOutlineStateVisibleRectKey + {{0, 0}, {186, 337}} + + PBXTopSmartGroupGIDs + + XCIncludePerspectivesSwitch + 1 + XCSharingToken + com.apple.Xcode.GFSharingToken + + GeometryConfiguration + + Frame + {{0, 0}, {203, 355}} + GroupTreeTableConfiguration + + MainColumn + 186 + + RubberWindowFrame + 373 269 690 397 0 0 1440 878 + + Module + PBXSmartGroupTreeModule + Proportion + 100% + + + Name + Morph + PreferredWidth + 300 + ServiceClasses + + XCModuleDock + PBXSmartGroupTreeModule + + TableOfContents + + 11E0B1FE06471DED0097A5F4 + + ToolbarConfiguration + xcode.toolbar.config.default.short + + + PerspectivesBarVisible + + ShelfIsVisible + + SourceDescription + file at '/System/Library/PrivateFrameworks/DevToolsInterface.framework/Versions/A/Resources/XCPerspectivesSpecificationMode1.xcperspec' + StatusbarIsVisible + + TimeStamp + 0.0 + ToolbarDisplayMode + 1 + ToolbarIsVisible + + ToolbarSizeMode + 1 + Type + Perspectives + UpdateMessage + The Default Workspace in this version of Xcode now includes support to hide and show the detail view (what has been referred to as the "Metro-Morph" feature). You must discard your current Default Workspace settings and update to the latest Default Workspace in order to gain this feature. Do you wish to update to the latest Workspace defaults for project '%@'? + WindowJustification + 5 + WindowOrderList + + /Users/jechter/Documents/proj/glLandscape/deliverable/gamexcode/game.xcodeproj + + WindowString + 465 134 636 657 0 0 1440 878 + WindowTools + + + FirstTimeWindowDisplayed + + Identifier + windowTool.build + IsVertical + + Layout + + + Dock + + + ContentConfiguration + + PBXProjectModuleGUID + 1CD0528F0623707200166675 + PBXProjectModuleLabel + + StatusBarVisibility + + + GeometryConfiguration + + Frame + {{0, 0}, {878, 297}} + RubberWindowFrame + 506 93 878 777 0 0 1440 878 + + Module + PBXNavigatorGroup + Proportion + 297pt + + + ContentConfiguration + + PBXBuildLogShowsTranscriptDefaultKey + {{0, 5}, {878, 429}} + PBXProjectModuleGUID + XCMainBuildResultsModuleGUID + PBXProjectModuleLabel + Build + XCBuildResultsTrigger_Collapse + 1021 + XCBuildResultsTrigger_Open + 1011 + + GeometryConfiguration + + Frame + {{0, 302}, {878, 434}} + RubberWindowFrame + 506 93 878 777 0 0 1440 878 + + Module + PBXBuildResultsModule + Proportion + 434pt + + + Proportion + 736pt + + + Name + Build Results + ServiceClasses + + PBXBuildResultsModule + + StatusbarIsVisible + + TableOfContents + + 7F0610980876ED78001EA95C + 7FF380C50BD93F6C0089E593 + 1CD0528F0623707200166675 + XCMainBuildResultsModuleGUID + + ToolbarConfiguration + xcode.toolbar.config.build + WindowString + 506 93 878 777 0 0 1440 878 + WindowToolGUID + 7F0610980876ED78001EA95C + WindowToolIsVisible + + + + FirstTimeWindowDisplayed + + Identifier + windowTool.debugger + IsVertical + + Layout + + + Dock + + + ContentConfiguration + + Debugger + + HorizontalSplitView + + _collapsingFrameDimension + 0.0 + _indexOfCollapsedView + 0 + _percentageOfCollapsedView + 0.0 + isCollapsed + yes + sizes + + {{0, 0}, {363, 334}} + {{363, 0}, {753, 334}} + + + VerticalSplitView + + _collapsingFrameDimension + 0.0 + _indexOfCollapsedView + 0 + _percentageOfCollapsedView + 0.0 + isCollapsed + yes + sizes + + {{0, 0}, {1116, 334}} + {{0, 334}, {1116, 331}} + + + + LauncherConfigVersion + 8 + PBXProjectModuleGUID + 1C162984064C10D400B95A72 + PBXProjectModuleLabel + Debug - GLUTExamples (Underwater) + + GeometryConfiguration + + DebugConsoleDrawerSize + {100, 120} + DebugConsoleVisible + None + DebugConsoleWindowFrame + {{200, 200}, {500, 300}} + DebugSTDIOWindowFrame + {{200, 200}, {500, 300}} + Frame + {{0, 0}, {1116, 665}} + RubberWindowFrame + 53 99 1116 706 0 0 1440 878 + + Module + PBXDebugSessionModule + Proportion + 665pt + + + Proportion + 665pt + + + Name + Debugger + ServiceClasses + + PBXDebugSessionModule + + StatusbarIsVisible + + TableOfContents + + 1CD10A99069EF8BA00B06720 + 7F58D1CA0B8B09D200714762 + 1C162984064C10D400B95A72 + 7F58D1CB0B8B09D200714762 + 7F58D1CC0B8B09D200714762 + 7F58D1CD0B8B09D200714762 + 7F58D1CE0B8B09D200714762 + 7F58D1CF0B8B09D200714762 + 7F58D1D00B8B09D200714762 + + ToolbarConfiguration + xcode.toolbar.config.debug + WindowString + 53 99 1116 706 0 0 1440 878 + WindowToolGUID + 1CD10A99069EF8BA00B06720 + WindowToolIsVisible + + + + FirstTimeWindowDisplayed + + Identifier + windowTool.find + IsVertical + + Layout + + + Dock + + + Dock + + + BecomeActive + + ContentConfiguration + + PBXProjectModuleGUID + 1CDD528C0622207200134675 + PBXProjectModuleLabel + collision.cpp + StatusBarVisibility + + + GeometryConfiguration + + Frame + {{0, 0}, {900, 217}} + RubberWindowFrame + 116 237 900 641 0 0 1440 878 + + Module + PBXNavigatorGroup + Proportion + 900pt + + + Proportion + 217pt + + + ContentConfiguration + + PBXProjectModuleGUID + 1CD0528E0623707200166675 + PBXProjectModuleLabel + Project Find + + GeometryConfiguration + + Frame + {{0, 222}, {900, 378}} + RubberWindowFrame + 116 237 900 641 0 0 1440 878 + + Module + PBXProjectFindModule + Proportion + 378pt + + + Proportion + 600pt + + + Name + Project Find + ServiceClasses + + PBXProjectFindModule + + StatusbarIsVisible + + TableOfContents + + 1C530D57069F1CE1000CFCEE + 7F5434790C17534C00BE8DBF + 7F54347A0C17534C00BE8DBF + 1CDD528C0622207200134675 + 1CD0528E0623707200166675 + + WindowString + 116 237 900 641 0 0 1440 878 + WindowToolGUID + 1C530D57069F1CE1000CFCEE + WindowToolIsVisible + + + + Identifier + MENUSEPARATOR + + + FirstTimeWindowDisplayed + + Identifier + windowTool.debuggerConsole + IsVertical + + Layout + + + Dock + + + ContentConfiguration + + PBXProjectModuleGUID + 1C78EAAC065D492600B07095 + PBXProjectModuleLabel + Debugger Console + + GeometryConfiguration + + Frame + {{0, 0}, {440, 358}} + RubberWindowFrame + 1000 94 440 400 0 0 1440 878 + + Module + PBXDebugCLIModule + Proportion + 358pt + + + Proportion + 359pt + + + Name + Debugger Console + ServiceClasses + + PBXDebugCLIModule + + StatusbarIsVisible + + TableOfContents + + 7FFFA9DE0886BF1C0046AA19 + 7F58D1D10B8B09D200714762 + 1C78EAAC065D492600B07095 + + WindowString + 1000 94 440 400 0 0 1440 878 + WindowToolGUID + 7FFFA9DE0886BF1C0046AA19 + WindowToolIsVisible + + + + FirstTimeWindowDisplayed + + Identifier + windowTool.run + IsVertical + + Layout + + + Dock + + + ContentConfiguration + + LauncherConfigVersion + 3 + PBXProjectModuleGUID + 1CD0528B0623707200166675 + PBXProjectModuleLabel + Run + Runner + + HorizontalSplitView + + _collapsingFrameDimension + 0.0 + _indexOfCollapsedView + 0 + _percentageOfCollapsedView + 0.0 + isCollapsed + yes + sizes + + {{0, 0}, {367, 168}} + {{0, 173}, {367, 270}} + + + VerticalSplitView + + _collapsingFrameDimension + 0.0 + _indexOfCollapsedView + 0 + _percentageOfCollapsedView + 0.0 + isCollapsed + yes + sizes + + {{0, 0}, {406, 443}} + {{411, 0}, {517, 443}} + + + + + GeometryConfiguration + + Frame + {{0, 0}, {643, 361}} + RubberWindowFrame + 795 162 643 402 0 0 1440 878 + + Module + PBXRunSessionModule + Proportion + 361pt + + + Proportion + 361pt + + + Name + Run Log + ServiceClasses + + PBXRunSessionModule + + StatusbarIsVisible + + TableOfContents + + 1C0AD2B3069F1EA900FABCE6 + 7FF380C60BD93F6C0089E593 + 1CD0528B0623707200166675 + 7FF380C70BD93F6C0089E593 + + ToolbarConfiguration + xcode.toolbar.config.run + WindowString + 795 162 643 402 0 0 1440 878 + WindowToolGUID + 1C0AD2B3069F1EA900FABCE6 + WindowToolIsVisible + + + + Identifier + windowTool.scm + Layout + + + Dock + + + ContentConfiguration + + PBXProjectModuleGUID + 1C78EAB2065D492600B07095 + PBXProjectModuleLabel + <No Editor> + PBXSplitModuleInNavigatorKey + + Split0 + + PBXProjectModuleGUID + 1C78EAB3065D492600B07095 + + SplitCount + 1 + + StatusBarVisibility + 1 + + GeometryConfiguration + + Frame + {{0, 0}, {452, 0}} + RubberWindowFrame + 743 379 452 308 0 0 1280 1002 + + Module + PBXNavigatorGroup + Proportion + 0pt + + + BecomeActive + 1 + ContentConfiguration + + PBXProjectModuleGUID + 1CD052920623707200166675 + PBXProjectModuleLabel + SCM + + GeometryConfiguration + + ConsoleFrame + {{0, 259}, {452, 0}} + Frame + {{0, 7}, {452, 259}} + RubberWindowFrame + 743 379 452 308 0 0 1280 1002 + TableConfiguration + + Status + 30 + FileName + 199 + Path + 197.09500122070312 + + TableFrame + {{0, 0}, {452, 250}} + + Module + PBXCVSModule + Proportion + 262pt + + + Proportion + 266pt + + + Name + SCM + ServiceClasses + + PBXCVSModule + + StatusbarIsVisible + 1 + TableOfContents + + 1C78EAB4065D492600B07095 + 1C78EAB5065D492600B07095 + 1C78EAB2065D492600B07095 + 1CD052920623707200166675 + + ToolbarConfiguration + xcode.toolbar.config.scm + WindowString + 743 379 452 308 0 0 1280 1002 + + + FirstTimeWindowDisplayed + + Identifier + windowTool.breakpoints + IsVertical + + Layout + + + Dock + + + ContentConfiguration + + PBXBottomSmartGroupGIDs + + 1C77FABC04509CD000000102 + + PBXProjectModuleGUID + 1CE0B1FE06471DED0097A5F4 + PBXProjectModuleLabel + Files + PBXProjectStructureProvided + no + PBXSmartGroupTreeModuleColumnData + + PBXSmartGroupTreeModuleColumnWidthsKey + + 168 + + PBXSmartGroupTreeModuleColumnsKey_v4 + + MainColumn + + + PBXSmartGroupTreeModuleOutlineStateKey_v7 + + PBXSmartGroupTreeModuleOutlineStateExpansionKey + + 1C77FABC04509CD000000102 + + PBXSmartGroupTreeModuleOutlineStateSelectionKey + + + 0 + + + PBXSmartGroupTreeModuleOutlineStateVisibleRectKey + {{0, 0}, {168, 350}} + + PBXTopSmartGroupGIDs + + XCIncludePerspectivesSwitch + + + GeometryConfiguration + + Frame + {{0, 0}, {185, 368}} + GroupTreeTableConfiguration + + MainColumn + 168 + + RubberWindowFrame + 517 174 744 409 0 0 1280 832 + + Module + PBXSmartGroupTreeModule + Proportion + 185pt + + + BecomeActive + + ContentConfiguration + + PBXProjectModuleGUID + 1CA1AED706398EBD00589147 + PBXProjectModuleLabel + Detail + + GeometryConfiguration + + Frame + {{190, 0}, {554, 368}} + RubberWindowFrame + 517 174 744 409 0 0 1280 832 + + Module + XCDetailModule + Proportion + 554pt + + + Proportion + 368pt + + + MajorVersion + 2 + MinorVersion + 0 + Name + Breakpoints + ServiceClasses + + PBXSmartGroupTreeModule + XCDetailModule + + StatusbarIsVisible + + TableOfContents + + 7F9E845E0AB879A1005F896D + 7F9E845F0AB879A1005F896D + 1CE0B1FE06471DED0097A5F4 + 1CA1AED706398EBD00589147 + + ToolbarConfiguration + xcode.toolbar.config.breakpoints + WindowString + 517 174 744 409 0 0 1280 832 + WindowToolGUID + 7F9E845E0AB879A1005F896D + WindowToolIsVisible + + + + FirstTimeWindowDisplayed + + Identifier + windowTool.debugAnimator + IsVertical + + Layout + + + Dock + + + ContentConfiguration + + PBXProjectModuleGUID + 7F09FF9D0934DCA600AB64D5 + PBXProjectModuleLabel + + StatusBarVisibility + + + GeometryConfiguration + + Frame + {{0, 0}, {700, 459}} + RubberWindowFrame + 154 309 700 500 0 0 1280 832 + + Module + PBXNavigatorGroup + Proportion + 459pt + + + Proportion + 459pt + + + Name + Debug Visualizer + ServiceClasses + + PBXNavigatorGroup + + StatusbarIsVisible + + TableOfContents + + 7F09FF9F0934DCA600AB64D5 + 7F09FFA00934DCA600AB64D5 + 7F09FF9D0934DCA600AB64D5 + + ToolbarConfiguration + xcode.toolbar.config.debugAnimator + WindowString + 154 309 700 500 0 0 1280 832 + WindowToolGUID + 7F09FF9F0934DCA600AB64D5 + WindowToolIsVisible + + + + Identifier + windowTool.bookmarks + Layout + + + Dock + + + Module + PBXBookmarksModule + Proportion + 100% + + + Proportion + 100% + + + Name + Bookmarks + ServiceClasses + + PBXBookmarksModule + + StatusbarIsVisible + 0 + WindowString + 538 42 401 187 0 0 1280 1002 + + + Identifier + windowTool.classBrowser + Layout + + + Dock + + + BecomeActive + 1 + ContentConfiguration + + OptionsSetName + Hierarchy, all classes + PBXProjectModuleGUID + 1CA6456E063B45B4001379D8 + PBXProjectModuleLabel + Class Browser - NSObject + + GeometryConfiguration + + ClassesFrame + {{0, 0}, {374, 96}} + ClassesTreeTableConfiguration + + PBXClassNameColumnIdentifier + 208 + PBXClassBookColumnIdentifier + 22 + + Frame + {{0, 0}, {630, 331}} + MembersFrame + {{0, 105}, {374, 395}} + MembersTreeTableConfiguration + + PBXMemberTypeIconColumnIdentifier + 22 + PBXMemberNameColumnIdentifier + 216 + PBXMemberTypeColumnIdentifier + 97 + PBXMemberBookColumnIdentifier + 22 + + PBXModuleWindowStatusBarHidden2 + 1 + RubberWindowFrame + 385 179 630 352 0 0 1440 878 + + Module + PBXClassBrowserModule + Proportion + 332pt + + + Proportion + 332pt + + + Name + Class Browser + ServiceClasses + + PBXClassBrowserModule + + StatusbarIsVisible + 0 + TableOfContents + + 1C0AD2AF069F1E9B00FABCE6 + 1C0AD2B0069F1E9B00FABCE6 + 1CA6456E063B45B4001379D8 + + ToolbarConfiguration + xcode.toolbar.config.classbrowser + WindowString + 385 179 630 352 0 0 1440 878 + WindowToolGUID + 1C0AD2AF069F1E9B00FABCE6 + WindowToolIsVisible + 0 + + + + diff --git a/game.xcodeproj/jechter.mode1v3 b/game.xcodeproj/jechter.mode1v3 new file mode 100644 index 0000000..05c891f --- /dev/null +++ b/game.xcodeproj/jechter.mode1v3 @@ -0,0 +1,1386 @@ + + + + + ActivePerspectiveName + Project + AllowedModules + + + BundleLoadPath + + MaxInstances + n + Module + PBXSmartGroupTreeModule + Name + Groups and Files Outline View + + + BundleLoadPath + + MaxInstances + n + Module + PBXNavigatorGroup + Name + Editor + + + BundleLoadPath + + MaxInstances + n + Module + XCTaskListModule + Name + Task List + + + BundleLoadPath + + MaxInstances + n + Module + XCDetailModule + Name + File and Smart Group Detail Viewer + + + BundleLoadPath + + MaxInstances + 1 + Module + PBXBuildResultsModule + Name + Detailed Build Results Viewer + + + BundleLoadPath + + MaxInstances + 1 + Module + PBXProjectFindModule + Name + Project Batch Find Tool + + + BundleLoadPath + + MaxInstances + n + Module + XCProjectFormatConflictsModule + Name + Project Format Conflicts List + + + BundleLoadPath + + MaxInstances + n + Module + PBXBookmarksModule + Name + Bookmarks Tool + + + BundleLoadPath + + MaxInstances + n + Module + PBXClassBrowserModule + Name + Class Browser + + + BundleLoadPath + + MaxInstances + n + Module + PBXCVSModule + Name + Source Code Control Tool + + + BundleLoadPath + + MaxInstances + n + Module + PBXDebugBreakpointsModule + Name + Debug Breakpoints Tool + + + BundleLoadPath + + MaxInstances + n + Module + XCDockableInspector + Name + Inspector + + + BundleLoadPath + + MaxInstances + n + Module + PBXOpenQuicklyModule + Name + Open Quickly Tool + + + BundleLoadPath + + MaxInstances + 1 + Module + PBXDebugSessionModule + Name + Debugger + + + BundleLoadPath + + MaxInstances + 1 + Module + PBXDebugCLIModule + Name + Debug Console + + + BundleLoadPath + + MaxInstances + n + Module + XCSnapshotModule + Name + Snapshots Tool + + + BundlePath + /Developer/Library/PrivateFrameworks/DevToolsInterface.framework/Resources + Description + DefaultDescriptionKey + DockingSystemVisible + + Extension + mode1v3 + FavBarConfig + + PBXProjectModuleGUID + 7FDA74060D044F2200736020 + XCBarModuleItemNames + + XCBarModuleItems + + + FirstTimeWindowDisplayed + + Identifier + com.apple.perspectives.project.mode1v3 + MajorVersion + 33 + MinorVersion + 0 + Name + Default + Notifications + + OpenEditors + + PerspectiveWidths + + -1 + -1 + + Perspectives + + + ChosenToolbarItems + + active-target-popup + active-buildstyle-popup + action + NSToolbarFlexibleSpaceItem + buildOrClean + build-and-goOrGo + com.apple.ide.PBXToolbarStopButton + get-info + toggle-editor + NSToolbarFlexibleSpaceItem + com.apple.pbx.toolbar.searchfield + + ControllerClassBaseName + + IconName + WindowOfProjectWithEditor + Identifier + perspective.project + IsVertical + + Layout + + + ContentConfiguration + + PBXBottomSmartGroupGIDs + + 1C37FBAC04509CD000000102 + 1C37FAAC04509CD000000102 + 1C08E77C0454961000C914BD + 1C37FABC05509CD000000102 + 1C37FABC05539CD112110102 + E2644B35053B69B200211256 + 1C37FABC04509CD000100104 + 1CC0EA4004350EF90044410B + 1CC0EA4004350EF90041110B + + PBXProjectModuleGUID + 1CE0B1FE06471DED0097A5F4 + PBXProjectModuleLabel + Files + PBXProjectStructureProvided + yes + PBXSmartGroupTreeModuleColumnData + + PBXSmartGroupTreeModuleColumnWidthsKey + + 382 + + PBXSmartGroupTreeModuleColumnsKey_v4 + + MainColumn + + + PBXSmartGroupTreeModuleOutlineStateKey_v7 + + PBXSmartGroupTreeModuleOutlineStateExpansionKey + + 20286C29FDCF999611CA2CEA + 7FE52CD00D579B7500F442A1 + 7F0613AC08771244001EA95C + 7F060FE20876ED46001EA95C + 20286C2CFDCF999611CA2CEA + 20286C32FDCF999611CA2CEA + 1C37FBAC04509CD000000102 + 1C37FABC05509CD000000102 + + PBXSmartGroupTreeModuleOutlineStateSelectionKey + + + 0 + + + PBXSmartGroupTreeModuleOutlineStateVisibleRectKey + {{0, 0}, {382, 584}} + + PBXTopSmartGroupGIDs + + XCIncludePerspectivesSwitch + + XCSharingToken + com.apple.Xcode.GFSharingToken + + GeometryConfiguration + + Frame + {{0, 0}, {399, 602}} + GroupTreeTableConfiguration + + MainColumn + 382 + + RubberWindowFrame + 186 235 976 643 0 0 1440 878 + + Module + PBXSmartGroupTreeModule + Proportion + 399pt + + + Dock + + + ContentConfiguration + + PBXProjectModuleGUID + 1CE0B20306471E060097A5F4 + PBXProjectModuleLabel + + PBXSplitModuleInNavigatorKey + + Split0 + + PBXProjectModuleGUID + 1CE0B20406471E060097A5F4 + PBXProjectModuleLabel + + + SplitCount + 1 + + StatusBarVisibility + + + GeometryConfiguration + + Frame + {{0, 0}, {572, 0}} + RubberWindowFrame + 186 235 976 643 0 0 1440 878 + + Module + PBXNavigatorGroup + Proportion + 0pt + + + BecomeActive + + ContentConfiguration + + PBXProjectModuleGUID + 1CE0B20506471E060097A5F4 + PBXProjectModuleLabel + Detail + + GeometryConfiguration + + Frame + {{0, 5}, {572, 597}} + RubberWindowFrame + 186 235 976 643 0 0 1440 878 + + Module + XCDetailModule + Proportion + 597pt + + + Proportion + 572pt + + + Name + Project + ServiceClasses + + XCModuleDock + PBXSmartGroupTreeModule + XCModuleDock + PBXNavigatorGroup + XCDetailModule + + TableOfContents + + 7F5E9AD70DD843AF00B522D5 + 1CE0B1FE06471DED0097A5F4 + 7F5E9AD80DD843AF00B522D5 + 1CE0B20306471E060097A5F4 + 1CE0B20506471E060097A5F4 + + ToolbarConfiguration + xcode.toolbar.config.defaultV3 + + + ControllerClassBaseName + + IconName + WindowOfProject + Identifier + perspective.morph + IsVertical + 0 + Layout + + + BecomeActive + 1 + ContentConfiguration + + PBXBottomSmartGroupGIDs + + 1C37FBAC04509CD000000102 + 1C37FAAC04509CD000000102 + 1C08E77C0454961000C914BD + 1C37FABC05509CD000000102 + 1C37FABC05539CD112110102 + E2644B35053B69B200211256 + 1C37FABC04509CD000100104 + 1CC0EA4004350EF90044410B + 1CC0EA4004350EF90041110B + + PBXProjectModuleGUID + 11E0B1FE06471DED0097A5F4 + PBXProjectModuleLabel + Files + PBXProjectStructureProvided + yes + PBXSmartGroupTreeModuleColumnData + + PBXSmartGroupTreeModuleColumnWidthsKey + + 186 + + PBXSmartGroupTreeModuleColumnsKey_v4 + + MainColumn + + + PBXSmartGroupTreeModuleOutlineStateKey_v7 + + PBXSmartGroupTreeModuleOutlineStateExpansionKey + + 29B97314FDCFA39411CA2CEA + 1C37FABC05509CD000000102 + + PBXSmartGroupTreeModuleOutlineStateSelectionKey + + + 0 + + + PBXSmartGroupTreeModuleOutlineStateVisibleRectKey + {{0, 0}, {186, 337}} + + PBXTopSmartGroupGIDs + + XCIncludePerspectivesSwitch + 1 + XCSharingToken + com.apple.Xcode.GFSharingToken + + GeometryConfiguration + + Frame + {{0, 0}, {203, 355}} + GroupTreeTableConfiguration + + MainColumn + 186 + + RubberWindowFrame + 373 269 690 397 0 0 1440 878 + + Module + PBXSmartGroupTreeModule + Proportion + 100% + + + Name + Morph + PreferredWidth + 300 + ServiceClasses + + XCModuleDock + PBXSmartGroupTreeModule + + TableOfContents + + 11E0B1FE06471DED0097A5F4 + + ToolbarConfiguration + xcode.toolbar.config.default.shortV3 + + + PerspectivesBarVisible + + ShelfIsVisible + + SourceDescription + file at '/Developer/Library/PrivateFrameworks/DevToolsInterface.framework/Resources/XCPerspectivesSpecificationMode1.xcperspec' + StatusbarIsVisible + + TimeStamp + 0.0 + ToolbarDisplayMode + 1 + ToolbarIsVisible + + ToolbarSizeMode + 1 + Type + Perspectives + UpdateMessage + The Default Workspace in this version of Xcode now includes support to hide and show the detail view (what has been referred to as the "Metro-Morph" feature). You must discard your current Default Workspace settings and update to the latest Default Workspace in order to gain this feature. Do you wish to update to the latest Workspace defaults for project '%@'? + WindowJustification + 5 + WindowOrderList + + 7F5E9ADB0DD843AF00B522D5 + 7F5E9ADC0DD843AF00B522D5 + 7F5E9ADD0DD843AF00B522D5 + 7F5E9ADE0DD843AF00B522D5 + 1C530D57069F1CE1000CFCEE + /Users/jechter/Documents/proj/glLandscape/deliverable/gamexcode/game.xcodeproj + + WindowString + 186 235 976 643 0 0 1440 878 + WindowToolsV3 + + + FirstTimeWindowDisplayed + + Identifier + windowTool.build + IsVertical + + Layout + + + Dock + + + ContentConfiguration + + PBXProjectModuleGUID + 1CD0528F0623707200166675 + PBXProjectModuleLabel + + StatusBarVisibility + + + GeometryConfiguration + + Frame + {{0, 0}, {1035, 98}} + RubberWindowFrame + 25 217 1035 616 0 0 1440 878 + + Module + PBXNavigatorGroup + Proportion + 98pt + + + BecomeActive + + ContentConfiguration + + PBXBuildLogShowsTranscriptDefaultKey + {{0, 140}, {1035, 332}} + PBXProjectModuleGUID + XCMainBuildResultsModuleGUID + PBXProjectModuleLabel + Build + XCBuildResultsTrigger_Collapse + 1021 + XCBuildResultsTrigger_Open + 1011 + + GeometryConfiguration + + Frame + {{0, 103}, {1035, 472}} + RubberWindowFrame + 25 217 1035 616 0 0 1440 878 + + Module + PBXBuildResultsModule + Proportion + 472pt + + + Proportion + 575pt + + + Name + Build Results + ServiceClasses + + PBXBuildResultsModule + + StatusbarIsVisible + + TableOfContents + + 7FE52CE60D579D4B00F442A1 + 7F6EB7090DBB0184008B14A9 + 1CD0528F0623707200166675 + XCMainBuildResultsModuleGUID + + ToolbarConfiguration + xcode.toolbar.config.buildV3 + WindowString + 25 217 1035 616 0 0 1440 878 + WindowToolGUID + 7FE52CE60D579D4B00F442A1 + WindowToolIsVisible + + + + FirstTimeWindowDisplayed + + Identifier + windowTool.debugger + IsVertical + + Layout + + + Dock + + + ContentConfiguration + + Debugger + + HorizontalSplitView + + _collapsingFrameDimension + 0.0 + _indexOfCollapsedView + 0 + _percentageOfCollapsedView + 0.0 + isCollapsed + yes + sizes + + {{0, 0}, {381, 318}} + {{381, 0}, {455, 318}} + + + VerticalSplitView + + _collapsingFrameDimension + 0.0 + _indexOfCollapsedView + 0 + _percentageOfCollapsedView + 0.0 + isCollapsed + yes + sizes + + {{0, 0}, {836, 318}} + {{0, 318}, {836, 283}} + + + + LauncherConfigVersion + 8 + PBXProjectModuleGUID + 1C162984064C10D400B95A72 + PBXProjectModuleLabel + Debug - GLUTExamples (Underwater) + + GeometryConfiguration + + DebugConsoleVisible + None + DebugConsoleWindowFrame + {{200, 200}, {500, 300}} + DebugSTDIOWindowFrame + {{200, 200}, {500, 300}} + Frame + {{0, 0}, {836, 601}} + PBXDebugSessionStackFrameViewKey + + DebugVariablesTableConfiguration + + Name + 120 + Value + 85 + Summary + 225 + + Frame + {{381, 0}, {455, 318}} + RubberWindowFrame + 17 79 836 642 0 0 1440 878 + + RubberWindowFrame + 17 79 836 642 0 0 1440 878 + + Module + PBXDebugSessionModule + Proportion + 601pt + + + Proportion + 601pt + + + Name + Debugger + ServiceClasses + + PBXDebugSessionModule + + StatusbarIsVisible + + TableOfContents + + 1CD10A99069EF8BA00B06720 + 7FED15BC0D8FF6CC002AD94E + 1C162984064C10D400B95A72 + 7FED15BD0D8FF6CC002AD94E + 7FED15BE0D8FF6CC002AD94E + 7FED15BF0D8FF6CC002AD94E + 7FED15C00D8FF6CC002AD94E + 7FED15C10D8FF6CC002AD94E + + ToolbarConfiguration + xcode.toolbar.config.debugV3 + WindowString + 17 79 836 642 0 0 1440 878 + WindowToolGUID + 1CD10A99069EF8BA00B06720 + WindowToolIsVisible + + + + FirstTimeWindowDisplayed + + Identifier + windowTool.find + IsVertical + + Layout + + + Dock + + + Dock + + + BecomeActive + + ContentConfiguration + + PBXProjectModuleGUID + 1CDD528C0622207200134675 + PBXProjectModuleLabel + carphysics.cpp + StatusBarVisibility + + + GeometryConfiguration + + Frame + {{0, 0}, {841, 447}} + RubberWindowFrame + 225 57 841 821 0 0 1440 878 + + Module + PBXNavigatorGroup + Proportion + 841pt + + + Proportion + 447pt + + + ContentConfiguration + + PBXProjectModuleGUID + 1CD0528E0623707200166675 + PBXProjectModuleLabel + Project Find + + GeometryConfiguration + + Frame + {{0, 452}, {841, 328}} + RubberWindowFrame + 225 57 841 821 0 0 1440 878 + + Module + PBXProjectFindModule + Proportion + 328pt + + + Proportion + 780pt + + + Name + Project Find + ServiceClasses + + PBXProjectFindModule + + StatusbarIsVisible + + TableOfContents + + 1C530D57069F1CE1000CFCEE + 7F5E9AD90DD843AF00B522D5 + 7F5E9ADA0DD843AF00B522D5 + 1CDD528C0622207200134675 + 1CD0528E0623707200166675 + + WindowString + 225 57 841 821 0 0 1440 878 + WindowToolGUID + 1C530D57069F1CE1000CFCEE + WindowToolIsVisible + + + + Identifier + MENUSEPARATOR + + + FirstTimeWindowDisplayed + + Identifier + windowTool.debuggerConsole + IsVertical + + Layout + + + Dock + + + BecomeActive + + ContentConfiguration + + PBXProjectModuleGUID + 1C78EAAC065D492600B07095 + PBXProjectModuleLabel + Debugger Console + + GeometryConfiguration + + Frame + {{0, 0}, {739, 451}} + RubberWindowFrame + -1374 431 739 492 -1440 150 1440 900 + + Module + PBXDebugCLIModule + Proportion + 451pt + + + Proportion + 451pt + + + Name + Debugger Console + ServiceClasses + + PBXDebugCLIModule + + StatusbarIsVisible + + TableOfContents + + 1C78EAAD065D492600B07095 + 7F895DE00D69C720007B1359 + 1C78EAAC065D492600B07095 + + ToolbarConfiguration + xcode.toolbar.config.consoleV3 + WindowString + -1374 431 739 492 -1440 150 1440 900 + WindowToolGUID + 1C78EAAD065D492600B07095 + WindowToolIsVisible + + + + Identifier + windowTool.snapshots + Layout + + + Dock + + + Module + XCSnapshotModule + Proportion + 100% + + + Proportion + 100% + + + Name + Snapshots + ServiceClasses + + XCSnapshotModule + + StatusbarIsVisible + Yes + ToolbarConfiguration + xcode.toolbar.config.snapshots + WindowString + 315 824 300 550 0 0 1440 878 + WindowToolIsVisible + Yes + + + Identifier + windowTool.scm + Layout + + + Dock + + + ContentConfiguration + + PBXProjectModuleGUID + 1C78EAB2065D492600B07095 + PBXProjectModuleLabel + <No Editor> + PBXSplitModuleInNavigatorKey + + Split0 + + PBXProjectModuleGUID + 1C78EAB3065D492600B07095 + + SplitCount + 1 + + StatusBarVisibility + 1 + + GeometryConfiguration + + Frame + {{0, 0}, {452, 0}} + RubberWindowFrame + 743 379 452 308 0 0 1280 1002 + + Module + PBXNavigatorGroup + Proportion + 0pt + + + BecomeActive + 1 + ContentConfiguration + + PBXProjectModuleGUID + 1CD052920623707200166675 + PBXProjectModuleLabel + SCM + + GeometryConfiguration + + ConsoleFrame + {{0, 259}, {452, 0}} + Frame + {{0, 7}, {452, 259}} + RubberWindowFrame + 743 379 452 308 0 0 1280 1002 + TableConfiguration + + Status + 30 + FileName + 199 + Path + 197.09500122070312 + + TableFrame + {{0, 0}, {452, 250}} + + Module + PBXCVSModule + Proportion + 262pt + + + Proportion + 266pt + + + Name + SCM + ServiceClasses + + PBXCVSModule + + StatusbarIsVisible + 1 + TableOfContents + + 1C78EAB4065D492600B07095 + 1C78EAB5065D492600B07095 + 1C78EAB2065D492600B07095 + 1CD052920623707200166675 + + ToolbarConfiguration + xcode.toolbar.config.scm + WindowString + 743 379 452 308 0 0 1280 1002 + + + Identifier + windowTool.breakpoints + IsVertical + 0 + Layout + + + Dock + + + BecomeActive + 1 + ContentConfiguration + + PBXBottomSmartGroupGIDs + + 1C77FABC04509CD000000102 + + PBXProjectModuleGUID + 1CE0B1FE06471DED0097A5F4 + PBXProjectModuleLabel + Files + PBXProjectStructureProvided + no + PBXSmartGroupTreeModuleColumnData + + PBXSmartGroupTreeModuleColumnWidthsKey + + 168 + + PBXSmartGroupTreeModuleColumnsKey_v4 + + MainColumn + + + PBXSmartGroupTreeModuleOutlineStateKey_v7 + + PBXSmartGroupTreeModuleOutlineStateExpansionKey + + 1C77FABC04509CD000000102 + + PBXSmartGroupTreeModuleOutlineStateSelectionKey + + + 0 + + + PBXSmartGroupTreeModuleOutlineStateVisibleRectKey + {{0, 0}, {168, 350}} + + PBXTopSmartGroupGIDs + + XCIncludePerspectivesSwitch + 0 + + GeometryConfiguration + + Frame + {{0, 0}, {185, 368}} + GroupTreeTableConfiguration + + MainColumn + 168 + + RubberWindowFrame + 315 424 744 409 0 0 1440 878 + + Module + PBXSmartGroupTreeModule + Proportion + 185pt + + + ContentConfiguration + + PBXProjectModuleGUID + 1CA1AED706398EBD00589147 + PBXProjectModuleLabel + Detail + + GeometryConfiguration + + Frame + {{190, 0}, {554, 368}} + RubberWindowFrame + 315 424 744 409 0 0 1440 878 + + Module + XCDetailModule + Proportion + 554pt + + + Proportion + 368pt + + + MajorVersion + 3 + MinorVersion + 0 + Name + Breakpoints + ServiceClasses + + PBXSmartGroupTreeModule + XCDetailModule + + StatusbarIsVisible + 1 + TableOfContents + + 1CDDB66807F98D9800BB5817 + 1CDDB66907F98D9800BB5817 + 1CE0B1FE06471DED0097A5F4 + 1CA1AED706398EBD00589147 + + ToolbarConfiguration + xcode.toolbar.config.breakpointsV3 + WindowString + 315 424 744 409 0 0 1440 878 + WindowToolGUID + 1CDDB66807F98D9800BB5817 + WindowToolIsVisible + 1 + + + Identifier + windowTool.debugAnimator + Layout + + + Dock + + + Module + PBXNavigatorGroup + Proportion + 100% + + + Proportion + 100% + + + Name + Debug Visualizer + ServiceClasses + + PBXNavigatorGroup + + StatusbarIsVisible + 1 + ToolbarConfiguration + xcode.toolbar.config.debugAnimatorV3 + WindowString + 100 100 700 500 0 0 1280 1002 + + + Identifier + windowTool.bookmarks + Layout + + + Dock + + + Module + PBXBookmarksModule + Proportion + 100% + + + Proportion + 100% + + + Name + Bookmarks + ServiceClasses + + PBXBookmarksModule + + StatusbarIsVisible + 0 + WindowString + 538 42 401 187 0 0 1280 1002 + + + Identifier + windowTool.projectFormatConflicts + Layout + + + Dock + + + Module + XCProjectFormatConflictsModule + Proportion + 100% + + + Proportion + 100% + + + Name + Project Format Conflicts + ServiceClasses + + XCProjectFormatConflictsModule + + StatusbarIsVisible + 0 + WindowContentMinSize + 450 300 + WindowString + 50 850 472 307 0 0 1440 877 + + + Identifier + windowTool.classBrowser + Layout + + + Dock + + + BecomeActive + 1 + ContentConfiguration + + OptionsSetName + Hierarchy, all classes + PBXProjectModuleGUID + 1CA6456E063B45B4001379D8 + PBXProjectModuleLabel + Class Browser - NSObject + + GeometryConfiguration + + ClassesFrame + {{0, 0}, {374, 96}} + ClassesTreeTableConfiguration + + PBXClassNameColumnIdentifier + 208 + PBXClassBookColumnIdentifier + 22 + + Frame + {{0, 0}, {630, 331}} + MembersFrame + {{0, 105}, {374, 395}} + MembersTreeTableConfiguration + + PBXMemberTypeIconColumnIdentifier + 22 + PBXMemberNameColumnIdentifier + 216 + PBXMemberTypeColumnIdentifier + 97 + PBXMemberBookColumnIdentifier + 22 + + PBXModuleWindowStatusBarHidden2 + 1 + RubberWindowFrame + 385 179 630 352 0 0 1440 878 + + Module + PBXClassBrowserModule + Proportion + 332pt + + + Proportion + 332pt + + + Name + Class Browser + ServiceClasses + + PBXClassBrowserModule + + StatusbarIsVisible + 0 + TableOfContents + + 1C0AD2AF069F1E9B00FABCE6 + 1C0AD2B0069F1E9B00FABCE6 + 1CA6456E063B45B4001379D8 + + ToolbarConfiguration + xcode.toolbar.config.classbrowser + WindowString + 385 179 630 352 0 0 1440 878 + WindowToolGUID + 1C0AD2AF069F1E9B00FABCE6 + WindowToolIsVisible + 0 + + + Identifier + windowTool.refactoring + IncludeInToolsMenu + 0 + Layout + + + Dock + + + BecomeActive + 1 + GeometryConfiguration + + Frame + {0, 0}, {500, 335} + RubberWindowFrame + {0, 0}, {500, 335} + + Module + XCRefactoringModule + Proportion + 100% + + + Proportion + 100% + + + Name + Refactoring + ServiceClasses + + XCRefactoringModule + + WindowString + 200 200 500 356 0 0 1920 1200 + + + + diff --git a/game.xcodeproj/jechter.pbxuser b/game.xcodeproj/jechter.pbxuser new file mode 100644 index 0000000..77c348a --- /dev/null +++ b/game.xcodeproj/jechter.pbxuser @@ -0,0 +1,1316 @@ +// !$*UTF8*$! +{ + 0867D6ABFE840B52C02AAC07 /* English */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1022, 724}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {1022, 724}}"; + }; + }; + 20286C28FDCF999611CA2CEA /* Project object */ = { + activeArchitecture = i386; + activeBuildConfigurationName = Release; + activeExecutable = 7F060FCC0876EC7B001EA95C /* Redline */; + activeTarget = 8D0C4E890486CD37000505A6 /* Redline */; + addToTargets = ( + 8D0C4E890486CD37000505A6 /* Redline */, + ); + breakpoints = ( + 7F7EDD3409EFF62400B2A665 /* stddebug.c:35 */, + 7F6A848109B5CBD500B7A730 /* network_NT.cpp:726 */, + 7FDFBCBC0A15F75B0022488F /* interface.cpp:459 */, + 7FDFBED20A1DEAA00022488F /* stddebug.c:35 */, + 7FD0688909B0A2C800E14DEA /* network_NT.cpp:46 */, + 7FD0689F09B0A48B00E14DEA /* network_NT.cpp:44 */, + 7FD068A109B0A48D00E14DEA /* network_NT.cpp:44 */, + 7FD068A309B0A48D00E14DEA /* network_NT.cpp:44 */, + 7FDC4BFC09D0328D00C9BA87 /* network_NT.cpp:607 */, + 7FFEB17B09FF771F00B59BC7 /* sound_ST.cpp:440 */, + 7FE438BC09BDEF5E00D51B22 /* textures.cpp:345 */, + 7FB88F230AA70EAF003FF2EE /* textures.cpp:345 */, + 7FC99FF40C40F5E7001DEEB0 /* malloc_error_break */, + 7FC9A0080C40F7D4001DEEB0 /* malloc_error_break */, + ); + codeSenseManager = 7F060FE10876EC7F001EA95C /* Code sense */; + executables = ( + 7F060FCC0876EC7B001EA95C /* Redline */, + ); + perUserDictionary = { + "PBXConfiguration.PBXBreakpointsDataSource.v1:1CA1AED706398EBD00589147" = { + PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; + PBXFileTableDataSourceColumnSortingKey = PBXBreakpointsDataSource_BreakpointID; + PBXFileTableDataSourceColumnWidthsKey = ( + 20, + 20, + 210, + 20, + 110, + 109, + 20, + ); + PBXFileTableDataSourceColumnsKey = ( + PBXBreakpointsDataSource_ActionID, + PBXBreakpointsDataSource_TypeID, + PBXBreakpointsDataSource_BreakpointID, + PBXBreakpointsDataSource_UseID, + PBXBreakpointsDataSource_LocationID, + PBXBreakpointsDataSource_ConditionID, + PBXBreakpointsDataSource_ContinueID, + ); + }; + PBXConfiguration.PBXFileTableDataSource3.PBXExecutablesDataSource = { + PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; + PBXFileTableDataSourceColumnSortingKey = PBXExecutablesDataSource_NameID; + PBXFileTableDataSourceColumnWidthsKey = ( + 22, + 300, + 207, + ); + PBXFileTableDataSourceColumnsKey = ( + PBXExecutablesDataSource_ActiveFlagID, + PBXExecutablesDataSource_NameID, + PBXExecutablesDataSource_CommentsID, + ); + }; + PBXConfiguration.PBXFileTableDataSource3.PBXFileTableDataSource = { + PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; + PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID; + PBXFileTableDataSourceColumnWidthsKey = ( + 20, + 333, + 20, + 48, + 43, + 43, + 20, + ); + PBXFileTableDataSourceColumnsKey = ( + PBXFileDataSource_FiletypeID, + PBXFileDataSource_Filename_ColumnID, + PBXFileDataSource_Built_ColumnID, + PBXFileDataSource_ObjectSize_ColumnID, + PBXFileDataSource_Errors_ColumnID, + PBXFileDataSource_Warnings_ColumnID, + PBXFileDataSource_Target_ColumnID, + ); + }; + PBXConfiguration.PBXFileTableDataSource3.PBXFindDataSource = { + PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; + PBXFileTableDataSourceColumnSortingKey = PBXFindDataSource_LocationID; + PBXFileTableDataSourceColumnWidthsKey = ( + 200, + 120, + ); + PBXFileTableDataSourceColumnsKey = ( + PBXFindDataSource_MessageID, + PBXFindDataSource_LocationID, + ); + }; + PBXConfiguration.PBXTargetDataSource.PBXTargetDataSource = { + PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; + PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID; + PBXFileTableDataSourceColumnWidthsKey = ( + 20, + 293, + 60, + 20, + 48, + 43, + 43, + ); + PBXFileTableDataSourceColumnsKey = ( + PBXFileDataSource_FiletypeID, + PBXFileDataSource_Filename_ColumnID, + PBXTargetDataSource_PrimaryAttribute, + PBXFileDataSource_Built_ColumnID, + PBXFileDataSource_ObjectSize_ColumnID, + PBXFileDataSource_Errors_ColumnID, + PBXFileDataSource_Warnings_ColumnID, + ); + }; + PBXPerProjectTemplateStateSaveDate = 232032240; + PBXWorkspaceStateSaveDate = 232032240; + }; + sourceControlManager = 7F060FE00876EC7F001EA95C /* Source Control */; + userBuildSettings = { + }; + }; + 7F00457A090FC26D002F3482 /* stddebug.c */ = { + isa = PBXFileReference; + fileEncoding = 30; + lastKnownFileType = sourcecode.c.c; + name = stddebug.c; + path = "/apps/dev/SDKs/Ambrosia-XCode-2005-10-25/platform/common/stddebug.c"; + sourceTree = ""; + }; + 7F060FCC0876EC7B001EA95C /* Redline */ = { + isa = PBXExecutable; + activeArgIndices = ( + ); + argumentStrings = ( + ); + autoAttachOnCrash = 1; + breakpointsEnabled = 1; + configStateDict = { + }; + customDataFormattersEnabled = 1; + debuggerPlugin = GDBDebugging; + disassemblyDisplayState = 0; + dylibVariantSuffix = ""; + enableDebugStr = 1; + environmentEntries = ( + ); + executableSystemSymbolLevel = 0; + executableUserSymbolLevel = 3; + libgmallocEnabled = 0; + name = Redline; + savedGlobals = { + }; + sourceDirectories = ( + ); + variableFormatDictionary = { + $v0 = 1; + $v1 = 1; + $v10 = 1; + $v11 = 1; + $v12 = 1; + $v13 = 1; + $v14 = 1; + $v15 = 1; + $v16 = 1; + $v17 = 1; + $v18 = 1; + $v19 = 1; + $v2 = 1; + $v20 = 1; + $v21 = 1; + $v22 = 1; + $v23 = 1; + $v24 = 1; + $v25 = 1; + $v26 = 1; + $v27 = 1; + $v28 = 1; + $v29 = 1; + $v3 = 1; + $v30 = 1; + $v31 = 1; + $v4 = 1; + $v5 = 1; + $v6 = 1; + $v7 = 1; + $v8 = 1; + $v9 = 1; + $vrsave = 1; + $vscr = 1; + }; + }; + 7F060FE00876EC7F001EA95C /* Source Control */ = { + isa = PBXSourceControlManager; + fallbackIsa = XCSourceControlManager; + isSCMEnabled = 0; + scmConfiguration = { + }; + scmType = ""; + }; + 7F060FE10876EC7F001EA95C /* Code sense */ = { + isa = PBXCodeSenseManager; + indexTemplatePath = ""; + }; + 7F060FE50876ED46001EA95C /* ai.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {974, 4046}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{0, 412}"; + sepNavVisRect = "{{0, 2561}, {859, 185}}"; + }; + }; + 7F060FEA0876ED46001EA95C /* carphysics.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1274, 18424}}"; + sepNavSelRange = "{26223, 10}"; + sepNavVisRange = "{25644, 1194}"; + sepNavVisRect = "{{0, 7681}, {794, 782}}"; + }; + }; + 7F060FEB0876ED46001EA95C /* carphysics.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1700, 3010}}"; + sepNavSelRange = "{1653, 9}"; + sepNavVisRange = "{1131, 1864}"; + sepNavVisRect = "{{0, 2323}, {859, 185}}"; + }; + }; + 7F060FEC0876ED46001EA95C /* carselection.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1104, 11564}}"; + sepNavSelRange = "{1793, 8}"; + sepNavVisRange = "{679, 1868}"; + sepNavVisRect = "{{0, 210}, {900, 763}}"; + }; + }; + 7F060FED0876ED46001EA95C /* carselection.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1160, 674}}"; + sepNavSelRange = "{113, 0}"; + sepNavVisRange = "{0, 905}"; + sepNavVisRect = "{{0, 0}, {1087, 764}}"; + }; + }; + 7F060FF70876ED46001EA95C /* challenges.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {980, 3458}}"; + sepNavSelRange = "{3278, 10}"; + sepNavVisRange = "{1490, 844}"; + sepNavVisRect = "{{0, 881}, {900, 763}}"; + }; + }; + 7F060FF80876ED46001EA95C /* challenges.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1086, 690}}"; + sepNavSelRange = "{232, 0}"; + sepNavVisRect = "{{0, 0}, {1086, 690}}"; + }; + }; + 7F060FF90876ED46001EA95C /* collision.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1154, 11340}}"; + sepNavSelRange = "{19378, 6}"; + sepNavVisRange = "{648, 2154}"; + sepNavVisRect = "{{0, 7818}, {859, 185}}"; + }; + }; + 7F060FFA0876ED46001EA95C /* collision.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {920, 742}}"; + sepNavSelRange = "{100, 36}"; + sepNavVisRect = "{{0, 0}, {920, 742}}"; + }; + }; + 7F060FFB0876ED46001EA95C /* config.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {909, 757}}"; + sepNavSelRange = "{1113, 0}"; + sepNavVisRect = "{{0, 0}, {909, 757}}"; + }; + }; + 7F060FFC0876ED46001EA95C /* config.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {789, 1498}}"; + sepNavSelRange = "{1304, 18}"; + sepNavVisRange = "{649, 1235}"; + sepNavVisRect = "{{0, 608}, {900, 763}}"; + }; + }; + 7F060FFD0876ED46001EA95C /* controls.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {908, 9632}}"; + sepNavSelRange = "{11536, 19}"; + sepNavVisRange = "{11058, 2002}"; + sepNavVisRect = "{{0, 5296}, {900, 763}}"; + }; + }; + 7F060FFE0876ED46001EA95C /* controls.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1092, 1400}}"; + sepNavSelRange = "{523, 19}"; + sepNavVisRange = "{0, 844}"; + sepNavVisRect = "{{0, 0}, {1137, 757}}"; + }; + }; + 7F060FFF0876ED46001EA95C /* entities.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {809, 785}}"; + sepNavSelRange = "{167, 42}"; + sepNavVisRect = "{{0, 0}, {809, 785}}"; + }; + }; + 7F0610000876ED46001EA95C /* entities.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1003, 1638}}"; + sepNavSelRange = "{880, 0}"; + sepNavVisRange = "{506, 1209}"; + sepNavVisRect = "{{0, 487}, {809, 785}}"; + }; + }; + 7F0610010876ED46001EA95C /* environment.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {848, 782}}"; + sepNavSelRange = "{364, 9}"; + sepNavVisRect = "{{0, 0}, {794, 782}}"; + }; + }; + 7F0610020876ED46001EA95C /* environment.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {920, 700}}"; + sepNavSelRange = "{926, 9}"; + sepNavVisRect = "{{0, 0}, {812, 700}}"; + }; + }; + 7F0610030876ED46001EA95C /* error.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1112, 715}}"; + sepNavSelRange = "{78, 18}"; + sepNavVisRect = "{{0, 0}, {1112, 715}}"; + }; + }; + 7F0610040876ED46001EA95C /* fileio.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {986, 5110}}"; + sepNavSelRange = "{2220, 18}"; + sepNavVisRange = "{6771, 1860}"; + sepNavVisRect = "{{0, 4229}, {741, 783}}"; + }; + }; + 7F0610050876ED46001EA95C /* fileio.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1104, 1176}}"; + sepNavSelRange = "{80, 30}"; + sepNavVisRange = "{0, 1146}"; + sepNavVisRect = "{{0, 0}, {900, 763}}"; + }; + }; + 7F0610060876ED46001EA95C /* gameframe.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1478, 11844}}"; + sepNavSelRange = "{5053, 12}"; + sepNavVisRange = "{3784, 1455}"; + sepNavVisRect = "{{0, 10213}, {809, 785}}"; + }; + }; + 7F0610070876ED46001EA95C /* gameframe.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {909, 757}}"; + sepNavSelRange = "{713, 0}"; + sepNavVisRect = "{{0, 0}, {909, 757}}"; + }; + }; + 7F0610080876ED46001EA95C /* gameinitexit.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {497, 4522}}"; + sepNavSelRange = "{9373, 6}"; + sepNavVisRange = "{0, 0}"; + sepNavVisRect = "{{0, 6737}, {492, 249}}"; + }; + }; + 7F0610090876ED46001EA95C /* gameinitexit.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1104, 2562}}"; + sepNavSelRange = "{2154, 11}"; + sepNavVisRange = "{1780, 1072}"; + sepNavVisRect = "{{0, 924}, {794, 738}}"; + }; + }; + 7F06100A0876ED46001EA95C /* gamemem.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1026, 794}}"; + sepNavSelRange = "{391, 63}"; + sepNavVisRange = "{0, 461}"; + sepNavVisRect = "{{0, 0}, {934, 674}}"; + }; + }; + 7F06100B0876ED46001EA95C /* gamesound.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {818, 730}}"; + sepNavSelRange = "{237, 13}"; + sepNavVisRect = "{{0, 0}, {818, 730}}"; + }; + }; + 7F06100C0876ED46001EA95C /* gamesystem.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1112, 715}}"; + sepNavSelRange = "{407, 0}"; + sepNavVisRect = "{{0, 0}, {1112, 715}}"; + }; + }; + 7F06100D0876ED46001EA95C /* gametime.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {812, 700}}"; + sepNavSelRange = "{117, 24}"; + sepNavVisRect = "{{0, 0}, {812, 700}}"; + }; + }; + 7F06100F0876ED46001EA95C /* infodisplay.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1274, 12516}}"; + sepNavSelRange = "{45704, 0}"; + sepNavVisRange = "{44586, 2102}"; + sepNavVisRect = "{{0, 1456}, {909, 757}}"; + }; + }; + 7F0610100876ED46001EA95C /* initexit.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1104, 2128}}"; + sepNavSelRange = "{2621, 85}"; + sepNavVisRange = "{3340, 1150}"; + sepNavVisRect = "{{0, 0}, {900, 763}}"; + }; + }; + 7F0610110876ED46001EA95C /* initexit.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {986, 771}}"; + sepNavSelRange = "{94, 28}"; + sepNavVisRange = "{0, 235}"; + sepNavVisRect = "{{0, 0}, {900, 763}}"; + }; + }; + 7F0610130876ED46001EA95C /* interface.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {876, 750}}"; + sepNavSelRange = "{68, 0}"; + sepNavVisRect = "{{0, 0}, {876, 750}}"; + }; + }; + 7F0610140876ED46001EA95C /* interfacemultiplayer.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1092, 31024}}"; + sepNavSelRange = "{5170, 19}"; + sepNavVisRange = "{4319, 2537}"; + sepNavVisRect = "{{4, 2921}, {859, 185}}"; + }; + }; + 7F0610150876ED46001EA95C /* interfaceoptions.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1712, 10332}}"; + sepNavSelRange = "{11728, 18}"; + sepNavVisRange = "{10064, 2921}"; + sepNavVisRect = "{{0, 329}, {1137, 757}}"; + }; + }; + 7F0610160876ED46001EA95C /* interfaceutil.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1104, 17458}}"; + sepNavSelRange = "{37548, 0}"; + sepNavVisRange = "{36913, 1213}"; + sepNavVisRect = "{{0, 6928}, {686, 782}}"; + }; + }; + 7F0610170876ED46001EA95C /* interfaceutil.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {956, 1820}}"; + sepNavSelRange = "{2075, 42}"; + sepNavVisRect = "{{0, 991}, {750, 757}}"; + }; + }; + 7F0610180876ED46001EA95C /* lights.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1034, 3304}}"; + sepNavSelRange = "{800, 10}"; + sepNavVisRect = "{{0, 82}, {920, 742}}"; + }; + }; + 7F0610190876ED46001EA95C /* lights.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {769, 730}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {769, 730}}"; + }; + }; + 7F06101A0876ED46001EA95C /* localtracker.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1180, 694}}"; + sepNavSelRange = "{150, 11}"; + sepNavVisRect = "{{0, 0}, {1180, 694}}"; + }; + }; + 7F06101B0876ED46001EA95C /* log.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1104, 4130}}"; + sepNavSelRange = "{5701, 0}"; + sepNavVisRange = "{5214, 984}"; + sepNavVisRect = "{{0, 2827}, {920, 742}}"; + }; + }; + 7F06101C0876ED46001EA95C /* log.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1092, 802}}"; + sepNavSelRange = "{418, 7}"; + sepNavVisRange = "{0, 463}"; + sepNavVisRect = "{{0, 0}, {1095, 737}}"; + }; + }; + 7F06101D0876ED46001EA95C /* maccontrols.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1190, 7392}}"; + sepNavSelRange = "{19306, 49}"; + sepNavVisRange = "{19106, 1960}"; + sepNavVisRect = "{{0, 7872}, {775, 765}}"; + }; + }; + 7F06101E0876ED46001EA95C /* macerror.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {506, 1624}}"; + sepNavSelRange = "{1596, 0}"; + sepNavVisRect = "{{0, 864}, {492, 249}}"; + }; + }; + 7F06101F0876ED46001EA95C /* macfileio.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {986, 5738}}"; + sepNavSelRange = "{4612, 0}"; + sepNavVisRange = "{0, 1315}"; + sepNavVisRect = "{{0, 3136}, {741, 783}}"; + }; + }; + 7F0610200876ED46001EA95C /* maclocaltracker.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1151, 6874}}"; + sepNavSelRange = "{7940, 0}"; + sepNavVisRect = "{{0, 2947}, {1151, 729}}"; + }; + }; + 7F0610210876ED46001EA95C /* macscreen.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1226, 4732}}"; + sepNavSelRange = "{4097, 0}"; + sepNavVisRange = "{3958, 1239}"; + sepNavVisRect = "{{0, 1175}, {920, 742}}"; + }; + }; + 7F0610220876ED46001EA95C /* macsystem.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {780, 13062}}"; + sepNavSelRange = "{17730, 10}"; + sepNavVisRange = "{17534, 771}"; + sepNavVisRect = "{{0, 10290}, {778, 742}}"; + }; + }; + 7F0610230876ED46001EA95C /* mactexturesimport.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1025, 1512}}"; + sepNavSelRange = "{281, 91}"; + sepNavVisRect = "{{0, 0}, {1025, 759}}"; + }; + }; + 7F0610240876ED46001EA95C /* main.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1092, 802}}"; + sepNavSelRange = "{81, 17}"; + sepNavVisRange = "{0, 124}"; + sepNavVisRect = "{{0, 0}, {1031, 237}}"; + }; + }; + 7F0610250876ED46001EA95C /* mapselection.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1104, 5306}}"; + sepNavSelRange = "{2339, 28}"; + sepNavVisRange = "{1519, 1319}"; + sepNavVisRect = "{{0, 158}, {900, 763}}"; + }; + }; + 7F0610260876ED46001EA95C /* mapselection.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {778, 742}}"; + sepNavSelRange = "{94, 31}"; + sepNavVisRect = "{{0, 0}, {778, 742}}"; + }; + }; + 7F0610270876ED46001EA95C /* models.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {812, 14378}}"; + sepNavSelRange = "{19565, 11}"; + sepNavVisRange = "{18759, 2023}"; + sepNavVisRect = "{{0, 13102}, {514, 299}}"; + }; + }; + 7F0610280876ED46001EA95C /* models.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {920, 742}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {920, 742}}"; + }; + }; + 7F0610290876ED46001EA95C /* modeltypes.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {750, 1092}}"; + sepNavSelRange = "{1774, 13}"; + sepNavVisRect = "{{0, 296}, {750, 757}}"; + }; + }; + 7F06102A0876ED46001EA95C /* music.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {835, 966}}"; + sepNavSelRange = "{823, 34}"; + sepNavVisRect = "{{0, 0}, {835, 685}}"; + }; + }; + 7F06102C0876ED46001EA95C /* network.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1026, 2044}}"; + sepNavSelRange = "{257, 24}"; + sepNavVisRange = "{0, 1073}"; + sepNavVisRect = "{{0, 1016}, {1095, 737}}"; + }; + }; + 7F06102E0876ED46001EA95C /* network_NT.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {970, 9534}}"; + sepNavSelRange = "{20108, 49}"; + sepNavVisRange = "{20045, 1238}"; + sepNavVisRect = "{{0, 3807}, {900, 763}}"; + }; + }; + 7F06102F0876ED46001EA95C /* networkphysics.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1026, 13482}}"; + sepNavSelRange = "{3665, 9}"; + sepNavVisRange = "{3180, 1545}"; + sepNavVisRect = "{{0, 4424}, {920, 742}}"; + }; + }; + 7F0610300876ED46001EA95C /* networkphysics.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1026, 794}}"; + sepNavSelRange = "{72, 95}"; + sepNavVisRange = "{0, 754}"; + sepNavVisRect = "{{0, 0}, {1031, 743}}"; + }; + }; + 7F0610310876ED46001EA95C /* parser.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {854, 12852}}"; + sepNavSelRange = "{25757, 13}"; + sepNavVisRange = "{23845, 2864}"; + sepNavVisRect = "{{0, 5377}, {809, 785}}"; + }; + }; + 7F0610320876ED46001EA95C /* parser.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {925, 711}}"; + sepNavSelRange = "{62, 21}"; + sepNavVisRect = "{{0, 0}, {925, 711}}"; + }; + }; + 7F0610330876ED46001EA95C /* particles.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1544, 6790}}"; + sepNavSelRange = "{9329, 13}"; + sepNavVisRect = "{{0, 5990}, {746, 733}}"; + }; + }; + 7F0610340876ED46001EA95C /* particles.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {978, 676}}"; + sepNavSelRange = "{277, 16}"; + sepNavVisRect = "{{0, 0}, {978, 676}}"; + }; + }; + 7F0610350876ED46001EA95C /* random.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {769, 1232}}"; + sepNavSelRange = "{1898, 31}"; + sepNavVisRect = "{{0, 502}, {769, 730}}"; + }; + }; + 7F0610370876ED46001EA95C /* rendercar.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {986, 9366}}"; + sepNavSelRange = "{13334, 9}"; + sepNavVisRange = "{11523, 1946}"; + sepNavVisRect = "{{0, 5409}, {746, 733}}"; + }; + }; + 7F0610380876ED46001EA95C /* rendercar.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {746, 733}}"; + sepNavSelRange = "{555, 71}"; + sepNavVisRect = "{{0, 0}, {746, 733}}"; + }; + }; + 7F0610390876ED46001EA95C /* renderframe.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1052, 10234}}"; + sepNavSelRange = "{9115, 1}"; + sepNavVisRange = "{19553, 1224}"; + sepNavVisRect = "{{0, 0}, {0, 0}}"; + }; + }; + 7F06103A0876ED46001EA95C /* renderframe.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {925, 756}}"; + sepNavSelRange = "{411, 19}"; + sepNavVisRect = "{{0, 0}, {925, 711}}"; + }; + }; + 7F06103B0876ED46001EA95C /* roads.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1026, 16044}}"; + sepNavSelRange = "{2781, 0}"; + sepNavVisRange = "{1408, 2426}"; + sepNavVisRect = "{{0, 3310}, {581, 522}}"; + }; + }; + 7F06103C0876ED46001EA95C /* roads.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1026, 1750}}"; + sepNavSelRange = "{1396, 13}"; + sepNavVisRange = "{502, 2214}"; + sepNavVisRect = "{{0, 319}, {920, 742}}"; + }; + }; + 7F06103D0876ED46001EA95C /* screen.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {900, 2058}}"; + sepNavSelRange = "{2667, 29}"; + sepNavVisRect = "{{0, 1295}, {900, 763}}"; + }; + }; + 7F06103E0876ED46001EA95C /* screen.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1045, 750}}"; + sepNavSelRange = "{788, 14}"; + sepNavVisRect = "{{0, 0}, {1045, 750}}"; + }; + }; + 7F0610400876ED46001EA95C /* sky.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {901, 4312}}"; + sepNavSelRange = "{6573, 16}"; + sepNavVisRect = "{{0, 2292}, {901, 706}}"; + }; + }; + 7F0610440876ED46001EA95C /* sound_ST.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1088, 6538}}"; + sepNavSelRange = "{12992, 18}"; + sepNavVisRect = "{{5, 5011}, {809, 785}}"; + }; + }; + 7F0610450876ED46001EA95C /* stencil.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1112, 896}}"; + sepNavSelRange = "{1466, 0}"; + sepNavVisRect = "{{0, 181}, {1112, 715}}"; + }; + }; + 7F0610460876ED46001EA95C /* stencil.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {818, 730}}"; + sepNavSelRange = "{65, 49}"; + sepNavVisRect = "{{0, 0}, {818, 730}}"; + }; + }; + 7F0610490876ED46001EA95C /* text.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1003, 5962}}"; + sepNavSelRange = "{5110, 0}"; + sepNavVisRange = "{4530, 1990}"; + sepNavVisRect = "{{0, 2610}, {900, 763}}"; + }; + }; + 7F06104A0876ED46001EA95C /* text.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {909, 757}}"; + sepNavSelRange = "{269, 0}"; + sepNavVisRect = "{{0, 0}, {909, 757}}"; + }; + }; + 7F06104B0876ED46001EA95C /* textures.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1003, 3668}}"; + sepNavSelRange = "{1355, 1}"; + sepNavVisRange = "{734, 1761}"; + sepNavVisRect = "{{0, 342}, {1025, 759}}"; + }; + }; + 7F06104C0876ED46001EA95C /* textures.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {920, 742}}"; + sepNavSelRange = "{269, 15}"; + sepNavVisRect = "{{0, 0}, {920, 742}}"; + }; + }; + 7F06104D0876ED46001EA95C /* texturesimport.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1095, 666}}"; + sepNavSelRange = "{68, 0}"; + sepNavVisRect = "{{0, 0}, {1095, 666}}"; + }; + }; + 7F06104E0876ED46001EA95C /* tire.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1004, 3500}}"; + sepNavSelRange = "{6697, 86}"; + sepNavVisRange = "{5517, 2703}"; + sepNavVisRect = "{{0, 2744}, {699, 756}}"; + }; + }; + 7F06104F0876ED46001EA95C /* tracker.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {780, 13244}}"; + sepNavSelRange = "{3807, 118}"; + sepNavVisRange = "{3544, 855}"; + sepNavVisRect = "{{0, 1287}, {859, 185}}"; + }; + }; + 7F0610500876ED46001EA95C /* tracker.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1226, 882}}"; + sepNavSelRange = "{129, 0}"; + sepNavVisRect = "{{0, 0}, {1226, 747}}"; + }; + }; + 7F0610510876ED46001EA95C /* tracks.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1095, 1932}}"; + sepNavSelRange = "{62, 0}"; + sepNavVisRect = "{{0, 0}, {1095, 666}}"; + }; + }; + 7F0610520876ED46001EA95C /* tracks.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {818, 730}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {818, 730}}"; + }; + }; + 7F0610530876ED46001EA95C /* transparency.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1598, 8694}}"; + sepNavSelRange = "{2152, 73}"; + sepNavVisRange = "{6815, 1942}"; + sepNavVisRect = "{{0, 3857}, {750, 757}}"; + }; + }; + 7F0610540876ED46001EA95C /* transparency.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {750, 840}}"; + sepNavSelRange = "{347, 0}"; + sepNavVisRect = "{{0, 0}, {750, 757}}"; + }; + }; + 7F0610550876ED46001EA95C /* vectors.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1026, 7784}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{0, 977}"; + sepNavVisRect = "{{0, 5927}, {900, 763}}"; + }; + }; + 7F0610560876ED46001EA95C /* vectors.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {820, 4270}}"; + sepNavSelRange = "{37, 16}"; + sepNavVisRect = "{{0, 112}, {820, 130}}"; + }; + }; + 7F0610570876ED46001EA95C /* writeout.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {789, 4772}}"; + sepNavSelRange = "{9658, 0}"; + sepNavVisRange = "{7589, 3439}"; + sepNavVisRect = "{{0, 2711}, {1137, 757}}"; + }; + }; + 7F06137C08770401001EA95C /* HID_Utilities_External.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1226, 6636}}"; + sepNavSelRange = "{11327, 16}"; + sepNavVisRect = "{{0, 0}, {1095, 666}}"; + }; + }; + 7F06138E08770577001EA95C /* interface.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1104, 8120}}"; + sepNavSelRange = "{15550, 7}"; + sepNavVisRange = "{14756, 1196}"; + sepNavVisRect = "{{0, 382}, {900, 763}}"; + }; + }; + 7F0613B708771262001EA95C /* compress.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {903, 2464}}"; + sepNavSelRange = "{11238, 0}"; + sepNavVisRect = "{{0, 1759}, {903, 705}}"; + }; + }; + 7F0613B808771263001EA95C /* LZRW.H */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {778, 3192}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {778, 742}}"; + }; + }; + 7F0613B908771263001EA95C /* LZRW3-A.C */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {837, 12600}}"; + sepNavSelRange = "{7865, 0}"; + sepNavVisRect = "{{0, 1414}, {837, 265}}"; + }; + }; + 7F16E74E0D57A68700706C33 /* rt3_redline.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1104, 29176}}"; + sepNavSelRange = "{6542, 0}"; + sepNavVisRange = "{5902, 1492}"; + }; + }; + 7F16E7640D57ABCC00706C33 /* ASWRegistrationCarbonAPI.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {775, 3374}}"; + sepNavSelRange = "{4905, 0}"; + sepNavVisRange = "{4657, 446}"; + }; + }; + 7F4348C5096D9B5B00C3981C /* GetPID.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {812, 5642}}"; + sepNavSelRange = "{19815, 51}"; + sepNavVisRect = "{{0, 4838}, {812, 700}}"; + }; + }; + 7F4348C6096D9B5B00C3981C /* GetPID.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {978, 2366}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {978, 676}}"; + }; + }; + 7F434A4C0973EC9900C3981C /* HID_cookie_strings.plist */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {859, 3444}}"; + sepNavSelRange = "{2280, 8}"; + sepNavVisRect = "{{0, 1315}, {859, 185}}"; + }; + }; + 7F434A4D0973EC9900C3981C /* HID_device_usage_strings.plist */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {859, 8876}}"; + sepNavSelRange = "{7602, 8}"; + sepNavVisRect = "{{0, 4381}, {859, 185}}"; + }; + }; + 7F6A848109B5CBD500B7A730 /* network_NT.cpp:726 */ = { + isa = PBXFileBreakpoint; + actions = ( + ); + breakpointStyle = 0; + continueAfterActions = 0; + countType = 0; + delayBeforeContinue = 0; + fileReference = 7F06102E0876ED46001EA95C /* network_NT.cpp */; + functionName = "NetworkReceivePacket()"; + hitCount = 0; + ignoreCount = 0; + lineNumber = 726; + modificationTime = 232036212.811644; + state = 2; + }; + 7F7EDD3209EFF62400B2A665 /* stddebug.c */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.c; + name = stddebug.c; + path = "/apps/dev/SDKs/Ambrosia-XCode-2006-04-13/platform/common/stddebug.c"; + sourceTree = ""; + }; + 7F7EDD3409EFF62400B2A665 /* stddebug.c:35 */ = { + isa = PBXFileBreakpoint; + actions = ( + ); + breakpointStyle = 0; + continueAfterActions = 0; + countType = 0; + delayBeforeContinue = 0; + fileReference = 7F7EDD3209EFF62400B2A665 /* stddebug.c */; + functionName = "Debug()"; + hitCount = 0; + ignoreCount = 0; + lineNumber = 35; + modificationTime = 232036212.811633; + state = 2; + }; + 7F7F050D09C9E5E3002D0EE3 /* interfacemultiplayer.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {909, 757}}"; + sepNavSelRange = "{263, 11}"; + sepNavVisRect = "{{0, 0}, {909, 757}}"; + }; + }; + 7F8C148C0A3ABBA000E76109 /* notifications.mm */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1026, 1134}}"; + sepNavSelRange = "{1859, 103}"; + sepNavVisRange = "{498, 1322}"; + sepNavVisRect = "{{0, 452}, {1007, 682}}"; + }; + }; + 7FB88F230AA70EAF003FF2EE /* textures.cpp:345 */ = { + isa = PBXFileBreakpoint; + actions = ( + ); + breakpointStyle = 0; + continueAfterActions = 0; + countType = 0; + delayBeforeContinue = 0; + fileReference = 7F06104B0876ED46001EA95C /* textures.cpp */; + functionName = "TexturesSelectTex()"; + hitCount = 0; + ignoreCount = 0; + lineNumber = 345; + location = Redline; + modificationTime = 232036212.811746; + state = 2; + }; + 7FBFFFDE08ACA6BB00618F96 /* ImmrHIDUtilAddOn.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1137, 1330}}"; + sepNavSelRange = "{1019, 12}"; + sepNavVisRect = "{{0, 573}, {1137, 757}}"; + }; + }; + 7FBFFFDF08ACA6BB00618F96 /* ImmrHIDUtilAddOn.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1095, 666}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {1095, 666}}"; + }; + }; + 7FC99FF40C40F5E7001DEEB0 /* malloc_error_break */ = { + isa = PBXSymbolicBreakpoint; + actions = ( + ); + breakpointStyle = 1; + continueAfterActions = 0; + countType = 0; + delayBeforeContinue = 0; + hitCount = 0; + ignoreCount = 0; + location = libSystem.B.dylib; + modificationTime = 224861110.153606; + state = 1; + symbolName = malloc_error_break; + }; + 7FC9A0080C40F7D4001DEEB0 /* malloc_error_break */ = { + isa = PBXSymbolicBreakpoint; + actions = ( + ); + breakpointStyle = 1; + continueAfterActions = 0; + countType = 0; + delayBeforeContinue = 0; + hitCount = 0; + ignoreCount = 0; + location = libSystem.B.dylib; + modificationTime = 224861110.1536201; + state = 1; + symbolName = malloc_error_break; + }; + 7FD0688909B0A2C800E14DEA /* network_NT.cpp:46 */ = { + isa = PBXFileBreakpoint; + actions = ( + ); + breakpointStyle = 0; + continueAfterActions = 0; + countType = 0; + delayBeforeContinue = 0; + fileReference = 7F06102E0876ED46001EA95C /* network_NT.cpp */; + functionName = "NetworkLockOutNewPlayers()"; + hitCount = 0; + ignoreCount = 0; + lineNumber = 46; + modificationTime = 232036212.811661; + state = 2; + }; + 7FD0689F09B0A48B00E14DEA /* network_NT.cpp:44 */ = { + isa = PBXFileBreakpoint; + actions = ( + ); + breakpointStyle = 0; + continueAfterActions = 0; + countType = 0; + delayBeforeContinue = 0; + fileReference = 7F06102E0876ED46001EA95C /* network_NT.cpp */; + functionName = ""; + hitCount = 0; + ignoreCount = 0; + lineNumber = 44; + modificationTime = 232036212.811666; + state = 2; + }; + 7FD068A109B0A48D00E14DEA /* network_NT.cpp:44 */ = { + isa = PBXFileBreakpoint; + actions = ( + ); + breakpointStyle = 0; + continueAfterActions = 0; + countType = 0; + delayBeforeContinue = 0; + fileReference = 7F06102E0876ED46001EA95C /* network_NT.cpp */; + functionName = ""; + hitCount = 0; + ignoreCount = 0; + lineNumber = 44; + modificationTime = 232036212.811671; + state = 2; + }; + 7FD068A309B0A48D00E14DEA /* network_NT.cpp:44 */ = { + isa = PBXFileBreakpoint; + actions = ( + ); + breakpointStyle = 0; + continueAfterActions = 0; + countType = 0; + delayBeforeContinue = 0; + fileReference = 7F06102E0876ED46001EA95C /* network_NT.cpp */; + functionName = ""; + hitCount = 0; + ignoreCount = 0; + lineNumber = 44; + modificationTime = 232036212.811677; + state = 2; + }; + 7FD85B0508BC760A00C3EB17 /* S3Decompression.cpp */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1031, 13748}}"; + sepNavSelRange = "{12170, 0}"; + sepNavVisRect = "{{0, 7051}, {1031, 249}}"; + }; + }; + 7FD85DEA08BC79EF00C3EB17 /* HID_Config_Utilities.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1086, 5964}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 5274}, {1086, 690}}"; + }; + }; + 7FD85DEC08BC79EF00C3EB17 /* HID_Name_Lookup.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1086, 4872}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {1086, 690}}"; + }; + }; + 7FD85DEE08BC79EF00C3EB17 /* HID_Transaction_Utilities.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1086, 4438}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 3748}, {1086, 690}}"; + }; + }; + 7FD85DEF08BC79EF00C3EB17 /* HID_Utilities.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1556, 30772}}"; + sepNavSelRange = "{2752, 40}"; + sepNavVisRect = "{{0, 160}, {1086, 690}}"; + }; + }; + 7FD85E2108BC9B8400C3EB17 /* fpu_exc.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1095, 666}}"; + sepNavSelRange = "{18, 19}"; + sepNavVisRect = "{{0, 0}, {1095, 666}}"; + }; + }; + 7FD85E2208BC9B8400C3EB17 /* fpu_exc.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1067, 673}}"; + sepNavSelRange = "{0, 526}"; + sepNavVisRect = "{{0, 0}, {1067, 673}}"; + }; + }; + 7FDC4BFC09D0328D00C9BA87 /* network_NT.cpp:607 */ = { + isa = PBXFileBreakpoint; + actions = ( + ); + breakpointStyle = 0; + continueAfterActions = 0; + countType = 0; + delayBeforeContinue = 0; + fileReference = 7F06102E0876ED46001EA95C /* network_NT.cpp */; + functionName = "JoinNetGame()"; + hitCount = 0; + ignoreCount = 0; + lineNumber = 607; + location = Redline; + modificationTime = 232036212.811681; + state = 2; + }; + 7FDFBCBC0A15F75B0022488F /* interface.cpp:459 */ = { + isa = PBXFileBreakpoint; + actions = ( + ); + breakpointStyle = 0; + continueAfterActions = 0; + countType = 0; + delayBeforeContinue = 0; + fileReference = 7F06138E08770577001EA95C /* interface.cpp */; + functionName = "InterfaceReplayMenu()"; + hitCount = 0; + ignoreCount = 0; + lineNumber = 459; + location = Redline; + modificationTime = 232036212.81165; + state = 2; + }; + 7FDFBED20A1DEAA00022488F /* stddebug.c:35 */ = { + isa = PBXFileBreakpoint; + actions = ( + ); + breakpointStyle = 0; + continueAfterActions = 0; + countType = 0; + delayBeforeContinue = 0; + fileReference = 7F00457A090FC26D002F3482 /* stddebug.c */; + functionName = "Debug()"; + hitCount = 0; + ignoreCount = 0; + lineNumber = 35; + location = Redline; + modificationTime = 232036212.811656; + state = 2; + }; + 7FE438BC09BDEF5E00D51B22 /* textures.cpp:345 */ = { + isa = PBXFileBreakpoint; + actions = ( + ); + breakpointStyle = 0; + continueAfterActions = 0; + countType = 0; + delayBeforeContinue = 0; + fileReference = 7F06104B0876ED46001EA95C /* textures.cpp */; + functionName = "TexturesSelectTex()"; + hitCount = 0; + ignoreCount = 0; + lineNumber = 345; + modificationTime = 232036212.811738; + state = 2; + }; + 7FE52CDD0D579B8800F442A1 /* reg_tool_3.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1092, 7714}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{20914, 2216}"; + }; + }; + 7FFEB17B09FF771F00B59BC7 /* sound_ST.cpp:440 */ = { + isa = PBXFileBreakpoint; + actions = ( + ); + breakpointStyle = 0; + continueAfterActions = 0; + countType = 0; + delayBeforeContinue = 0; + fileReference = 7F0610440876ED46001EA95C /* sound_ST.cpp */; + functionName = "SoundFreeEntitySources()"; + hitCount = 0; + ignoreCount = 0; + lineNumber = 440; + location = Redline; + modificationTime = 232036212.811687; + state = 2; + }; + 8D0C4E890486CD37000505A6 /* Redline */ = { + activeExec = 0; + executables = ( + 7F060FCC0876EC7B001EA95C /* Redline */, + ); + }; + 8D0C4E960486CD37000505A6 /* Info.plist */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1092, 4116}}"; + sepNavSelRange = "{1332, 0}"; + sepNavVisRange = "{0, 1589}"; + sepNavVisRect = "{{0, 0}, {1087, 764}}"; + }; + }; +} diff --git a/game.xcodeproj/project.pbxproj b/game.xcodeproj/project.pbxproj new file mode 100644 index 0000000..7cdd0a7 --- /dev/null +++ b/game.xcodeproj/project.pbxproj @@ -0,0 +1,855 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 42; + objects = { + +/* Begin PBXBuildFile section */ + 7F06105A0876ED46001EA95C /* ai.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F060FE50876ED46001EA95C /* ai.cpp */; }; + 7F06105D0876ED46001EA95C /* carphysics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F060FEA0876ED46001EA95C /* carphysics.cpp */; }; + 7F06105E0876ED46001EA95C /* carselection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F060FEC0876ED46001EA95C /* carselection.cpp */; }; + 7F0610640876ED46001EA95C /* collision.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F060FF90876ED46001EA95C /* collision.cpp */; }; + 7F0610650876ED46001EA95C /* config.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F060FFB0876ED46001EA95C /* config.cpp */; }; + 7F0610660876ED46001EA95C /* controls.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F060FFD0876ED46001EA95C /* controls.cpp */; }; + 7F0610670876ED46001EA95C /* entities.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F060FFF0876ED46001EA95C /* entities.cpp */; }; + 7F0610680876ED46001EA95C /* environment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F0610010876ED46001EA95C /* environment.cpp */; }; + 7F0610690876ED46001EA95C /* fileio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F0610040876ED46001EA95C /* fileio.cpp */; }; + 7F06106A0876ED46001EA95C /* gameframe.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F0610060876ED46001EA95C /* gameframe.cpp */; }; + 7F06106B0876ED46001EA95C /* gameinitexit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F0610080876ED46001EA95C /* gameinitexit.cpp */; }; + 7F06106D0876ED46001EA95C /* infodisplay.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F06100F0876ED46001EA95C /* infodisplay.cpp */; }; + 7F06106E0876ED46001EA95C /* initexit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F0610100876ED46001EA95C /* initexit.cpp */; }; + 7F0610700876ED46001EA95C /* interfacemultiplayer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F0610140876ED46001EA95C /* interfacemultiplayer.cpp */; }; + 7F0610710876ED46001EA95C /* interfaceoptions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F0610150876ED46001EA95C /* interfaceoptions.cpp */; }; + 7F0610720876ED46001EA95C /* interfaceutil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F0610160876ED46001EA95C /* interfaceutil.cpp */; }; + 7F0610730876ED46001EA95C /* lights.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F0610180876ED46001EA95C /* lights.cpp */; }; + 7F0610740876ED46001EA95C /* log.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F06101B0876ED46001EA95C /* log.cpp */; }; + 7F0610750876ED46001EA95C /* maccontrols.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F06101D0876ED46001EA95C /* maccontrols.cpp */; }; + 7F0610760876ED46001EA95C /* macerror.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F06101E0876ED46001EA95C /* macerror.cpp */; }; + 7F0610770876ED46001EA95C /* macfileio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F06101F0876ED46001EA95C /* macfileio.cpp */; }; + 7F0610780876ED46001EA95C /* maclocaltracker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F0610200876ED46001EA95C /* maclocaltracker.cpp */; }; + 7F0610790876ED46001EA95C /* macscreen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F0610210876ED46001EA95C /* macscreen.cpp */; }; + 7F06107A0876ED46001EA95C /* macsystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F0610220876ED46001EA95C /* macsystem.cpp */; }; + 7F06107B0876ED46001EA95C /* mactexturesimport.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F0610230876ED46001EA95C /* mactexturesimport.cpp */; }; + 7F06107C0876ED46001EA95C /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F0610240876ED46001EA95C /* main.cpp */; }; + 7F06107D0876ED46001EA95C /* mapselection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F0610250876ED46001EA95C /* mapselection.cpp */; }; + 7F06107E0876ED46001EA95C /* models.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F0610270876ED46001EA95C /* models.cpp */; }; + 7F06107F0876ED46001EA95C /* music.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F06102A0876ED46001EA95C /* music.cpp */; }; + 7F0610810876ED46001EA95C /* network_NT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F06102E0876ED46001EA95C /* network_NT.cpp */; }; + 7F0610820876ED46001EA95C /* networkphysics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F06102F0876ED46001EA95C /* networkphysics.cpp */; }; + 7F0610830876ED46001EA95C /* parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F0610310876ED46001EA95C /* parser.cpp */; }; + 7F0610840876ED46001EA95C /* particles.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F0610330876ED46001EA95C /* particles.cpp */; }; + 7F0610850876ED46001EA95C /* random.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F0610350876ED46001EA95C /* random.cpp */; }; + 7F0610860876ED46001EA95C /* rendercar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F0610370876ED46001EA95C /* rendercar.cpp */; }; + 7F0610870876ED46001EA95C /* renderframe.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F0610390876ED46001EA95C /* renderframe.cpp */; }; + 7F0610880876ED46001EA95C /* roads.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F06103B0876ED46001EA95C /* roads.cpp */; }; + 7F0610890876ED46001EA95C /* screen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F06103D0876ED46001EA95C /* screen.cpp */; }; + 7F06108A0876ED46001EA95C /* sky.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F0610400876ED46001EA95C /* sky.cpp */; }; + 7F06108D0876ED46001EA95C /* sound_ST.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F0610440876ED46001EA95C /* sound_ST.cpp */; }; + 7F06108E0876ED46001EA95C /* stencil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F0610450876ED46001EA95C /* stencil.cpp */; }; + 7F0610900876ED46001EA95C /* text.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F0610490876ED46001EA95C /* text.cpp */; }; + 7F0610910876ED46001EA95C /* textures.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F06104B0876ED46001EA95C /* textures.cpp */; }; + 7F0610920876ED46001EA95C /* tire.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F06104E0876ED46001EA95C /* tire.cpp */; }; + 7F0610930876ED46001EA95C /* tracker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F06104F0876ED46001EA95C /* tracker.cpp */; }; + 7F0610940876ED46001EA95C /* tracks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F0610510876ED46001EA95C /* tracks.cpp */; }; + 7F0610950876ED46001EA95C /* transparency.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F0610530876ED46001EA95C /* transparency.cpp */; }; + 7F0610960876ED46001EA95C /* vectors.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F0610550876ED46001EA95C /* vectors.cpp */; }; + 7F0610970876ED46001EA95C /* writeout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F0610570876ED46001EA95C /* writeout.cpp */; }; + 7F06138F08770577001EA95C /* interface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F06138E08770577001EA95C /* interface.cpp */; }; + 7F0613BB08771263001EA95C /* LZRW3-A.C in Sources */ = {isa = PBXBuildFile; fileRef = 7F0613B908771263001EA95C /* LZRW3-A.C */; }; + 7F4348C7096D9B5B00C3981C /* GetPID.c in Sources */ = {isa = PBXBuildFile; fileRef = 7F4348C5096D9B5B00C3981C /* GetPID.c */; }; + 7F434A4F0973EC9900C3981C /* HID_cookie_strings.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7F434A4C0973EC9900C3981C /* HID_cookie_strings.plist */; }; + 7F434A500973EC9900C3981C /* HID_device_usage_strings.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7F434A4D0973EC9900C3981C /* HID_device_usage_strings.plist */; }; + 7F434A510973EC9900C3981C /* HID_usage_strings.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7F434A4E0973EC9900C3981C /* HID_usage_strings.plist */; }; + 7F55FFDB08DDDA9700B82C72 /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A9504C8FFE6A3BC11CA0CBA /* ApplicationServices.framework */; }; + 7F55FFDC08DDDA9800B82C72 /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A9504CAFFE6A41611CA0CBA /* CoreServices.framework */; }; + 7F6EB7060DBB0141008B14A9 /* challenges.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F060FF70876ED46001EA95C /* challenges.cpp */; }; + 7F7EDC8309EE96BA00B2A665 /* Prev Track.scpt in Resources */ = {isa = PBXBuildFile; fileRef = 7F7EDC7F09EE95E000B2A665 /* Prev Track.scpt */; }; + 7F8C148D0A3ABBA000E76109 /* notifications.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7F8C148C0A3ABBA000E76109 /* notifications.mm */; }; + 7F8C14A70A3ABDF900E76109 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F8C14A60A3ABDF900E76109 /* Cocoa.framework */; }; + 7F9E3C5B095334BF000394C1 /* Next Track.scpt in Resources */ = {isa = PBXBuildFile; fileRef = 7F9E3C4F095331F7000394C1 /* Next Track.scpt */; }; + 7F9E3C7309534CB2000394C1 /* Status.scpt in Resources */ = {isa = PBXBuildFile; fileRef = 7F9E3C7209534CA8000394C1 /* Status.scpt */; }; + 7F9E3C7409534CB5000394C1 /* PlayPause.scpt in Resources */ = {isa = PBXBuildFile; fileRef = 7F9E3C7109534C98000394C1 /* PlayPause.scpt */; }; + 7FBFFFE008ACA6BB00618F96 /* ImmrHIDUtilAddOn.c in Sources */ = {isa = PBXBuildFile; fileRef = 7FBFFFDE08ACA6BB00618F96 /* ImmrHIDUtilAddOn.c */; }; + 7FC1DAFD087820860029047D /* AGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FC1DAFC087820860029047D /* AGL.framework */; }; + 7FC1DB0E087820970029047D /* QuickTime.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FC1DB0D087820970029047D /* QuickTime.framework */; }; + 7FC1DB330878209F0029047D /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FC1DB320878209F0029047D /* OpenGL.framework */; }; + 7FC1DC3708782A5C0029047D /* ForceFeedback.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FC1DC3608782A5C0029047D /* ForceFeedback.framework */; }; + 7FC5FD450D49131300C76FF4 /* AmbrosiaTools.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FC5FD440D49131300C76FF4 /* AmbrosiaTools.framework */; }; + 7FD85AC008BC697A00C3EB17 /* Redline.rsrc in Resources */ = {isa = PBXBuildFile; fileRef = 7FD85ABF08BC697A00C3EB17 /* Redline.rsrc */; }; + 7FD85B0708BC760A00C3EB17 /* S3Decompression.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7FD85B0508BC760A00C3EB17 /* S3Decompression.cpp */; }; + 7FD85D4208BC77B900C3EB17 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FD85D4108BC77B900C3EB17 /* IOKit.framework */; }; + 7FD85DF008BC79EF00C3EB17 /* HID_Config_Utilities.c in Sources */ = {isa = PBXBuildFile; fileRef = 7FD85DEA08BC79EF00C3EB17 /* HID_Config_Utilities.c */; }; + 7FD85DF108BC79EF00C3EB17 /* HID_Error_Handler.c in Sources */ = {isa = PBXBuildFile; fileRef = 7FD85DEB08BC79EF00C3EB17 /* HID_Error_Handler.c */; }; + 7FD85DF208BC79EF00C3EB17 /* HID_Name_Lookup.c in Sources */ = {isa = PBXBuildFile; fileRef = 7FD85DEC08BC79EF00C3EB17 /* HID_Name_Lookup.c */; }; + 7FD85DF308BC79EF00C3EB17 /* HID_Queue_Utilities.c in Sources */ = {isa = PBXBuildFile; fileRef = 7FD85DED08BC79EF00C3EB17 /* HID_Queue_Utilities.c */; }; + 7FD85DF408BC79EF00C3EB17 /* HID_Transaction_Utilities.c in Sources */ = {isa = PBXBuildFile; fileRef = 7FD85DEE08BC79EF00C3EB17 /* HID_Transaction_Utilities.c */; }; + 7FD85DF508BC79EF00C3EB17 /* HID_Utilities.c in Sources */ = {isa = PBXBuildFile; fileRef = 7FD85DEF08BC79EF00C3EB17 /* HID_Utilities.c */; }; + 7FD85E2308BC9B8400C3EB17 /* fpu_exc.c in Sources */ = {isa = PBXBuildFile; fileRef = 7FD85E2208BC9B8400C3EB17 /* fpu_exc.c */; }; + 7FDFBE480A1BA49C0022488F /* libSystemStubs.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FDFBE470A1BA49C0022488F /* libSystemStubs.a */; }; + 7FE3424609AF93CD006C8583 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FE3424509AF93CD006C8583 /* Security.framework */; }; + 7FED158B0D8FEA1F002AD94E /* librt3_nonag.o in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F16E75E0D57AA8200706C33 /* librt3_nonag.o */; }; + 7FFFAA020886C5F70046AA19 /* Plugin.icns in Resources */ = {isa = PBXBuildFile; fileRef = 7FFFAA000886C5F70046AA19 /* Plugin.icns */; }; + 7FFFAA030886C5F70046AA19 /* Redline.icns in Resources */ = {isa = PBXBuildFile; fileRef = 7FFFAA010886C5F70046AA19 /* Redline.icns */; }; + 8D0C4E8D0486CD37000505A6 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0867D6AAFE840B52C02AAC07 /* InfoPlist.strings */; }; + 8D0C4E920486CD37000505A6 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 20286C33FDCF999611CA2CEA /* Carbon.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 0867D6ABFE840B52C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; + 20286C33FDCF999611CA2CEA /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = ""; }; + 4A9504C8FFE6A3BC11CA0CBA /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = /System/Library/Frameworks/ApplicationServices.framework; sourceTree = ""; }; + 4A9504CAFFE6A41611CA0CBA /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = /System/Library/Frameworks/CoreServices.framework; sourceTree = ""; }; + 7F060FE50876ED46001EA95C /* ai.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = ai.cpp; sourceTree = ""; }; + 7F060FEA0876ED46001EA95C /* carphysics.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = carphysics.cpp; sourceTree = ""; }; + 7F060FEB0876ED46001EA95C /* carphysics.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = carphysics.h; sourceTree = ""; }; + 7F060FEC0876ED46001EA95C /* carselection.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = carselection.cpp; sourceTree = ""; }; + 7F060FED0876ED46001EA95C /* carselection.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = carselection.h; sourceTree = ""; }; + 7F060FF70876ED46001EA95C /* challenges.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = challenges.cpp; sourceTree = ""; }; + 7F060FF80876ED46001EA95C /* challenges.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = challenges.h; sourceTree = ""; }; + 7F060FF90876ED46001EA95C /* collision.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = collision.cpp; sourceTree = ""; }; + 7F060FFA0876ED46001EA95C /* collision.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = collision.h; sourceTree = ""; }; + 7F060FFB0876ED46001EA95C /* config.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = config.cpp; sourceTree = ""; }; + 7F060FFC0876ED46001EA95C /* config.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = config.h; sourceTree = ""; }; + 7F060FFD0876ED46001EA95C /* controls.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = controls.cpp; sourceTree = ""; }; + 7F060FFE0876ED46001EA95C /* controls.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = controls.h; sourceTree = ""; }; + 7F060FFF0876ED46001EA95C /* entities.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = entities.cpp; sourceTree = ""; }; + 7F0610000876ED46001EA95C /* entities.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = entities.h; sourceTree = ""; }; + 7F0610010876ED46001EA95C /* environment.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = environment.cpp; sourceTree = ""; }; + 7F0610020876ED46001EA95C /* environment.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = environment.h; sourceTree = ""; }; + 7F0610030876ED46001EA95C /* error.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = error.h; sourceTree = ""; }; + 7F0610040876ED46001EA95C /* fileio.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = fileio.cpp; sourceTree = ""; }; + 7F0610050876ED46001EA95C /* fileio.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = fileio.h; sourceTree = ""; }; + 7F0610060876ED46001EA95C /* gameframe.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = gameframe.cpp; sourceTree = ""; }; + 7F0610070876ED46001EA95C /* gameframe.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = gameframe.h; sourceTree = ""; }; + 7F0610080876ED46001EA95C /* gameinitexit.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = gameinitexit.cpp; sourceTree = ""; }; + 7F0610090876ED46001EA95C /* gameinitexit.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = gameinitexit.h; sourceTree = ""; }; + 7F06100A0876ED46001EA95C /* gamemem.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = gamemem.h; sourceTree = ""; }; + 7F06100B0876ED46001EA95C /* gamesound.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = gamesound.h; sourceTree = ""; }; + 7F06100C0876ED46001EA95C /* gamesystem.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = gamesystem.h; sourceTree = ""; }; + 7F06100D0876ED46001EA95C /* gametime.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = gametime.h; sourceTree = ""; }; + 7F06100F0876ED46001EA95C /* infodisplay.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = infodisplay.cpp; sourceTree = ""; }; + 7F0610100876ED46001EA95C /* initexit.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = initexit.cpp; sourceTree = ""; }; + 7F0610110876ED46001EA95C /* initexit.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = initexit.h; sourceTree = ""; }; + 7F0610130876ED46001EA95C /* interface.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = interface.h; sourceTree = ""; }; + 7F0610140876ED46001EA95C /* interfacemultiplayer.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = interfacemultiplayer.cpp; sourceTree = ""; }; + 7F0610150876ED46001EA95C /* interfaceoptions.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = interfaceoptions.cpp; sourceTree = ""; }; + 7F0610160876ED46001EA95C /* interfaceutil.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = interfaceutil.cpp; sourceTree = ""; }; + 7F0610170876ED46001EA95C /* interfaceutil.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = interfaceutil.h; sourceTree = ""; }; + 7F0610180876ED46001EA95C /* lights.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = lights.cpp; sourceTree = ""; }; + 7F0610190876ED46001EA95C /* lights.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = lights.h; sourceTree = ""; }; + 7F06101A0876ED46001EA95C /* localtracker.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = localtracker.h; sourceTree = ""; }; + 7F06101B0876ED46001EA95C /* log.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = log.cpp; sourceTree = ""; }; + 7F06101C0876ED46001EA95C /* log.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = log.h; sourceTree = ""; }; + 7F06101D0876ED46001EA95C /* maccontrols.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = maccontrols.cpp; sourceTree = ""; }; + 7F06101E0876ED46001EA95C /* macerror.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = macerror.cpp; sourceTree = ""; }; + 7F06101F0876ED46001EA95C /* macfileio.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = macfileio.cpp; sourceTree = ""; }; + 7F0610200876ED46001EA95C /* maclocaltracker.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = maclocaltracker.cpp; sourceTree = ""; }; + 7F0610210876ED46001EA95C /* macscreen.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = macscreen.cpp; sourceTree = ""; }; + 7F0610220876ED46001EA95C /* macsystem.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = macsystem.cpp; sourceTree = ""; }; + 7F0610230876ED46001EA95C /* mactexturesimport.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = mactexturesimport.cpp; sourceTree = ""; }; + 7F0610240876ED46001EA95C /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; + 7F0610250876ED46001EA95C /* mapselection.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = mapselection.cpp; sourceTree = ""; }; + 7F0610260876ED46001EA95C /* mapselection.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = mapselection.h; sourceTree = ""; }; + 7F0610270876ED46001EA95C /* models.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = models.cpp; sourceTree = ""; }; + 7F0610280876ED46001EA95C /* models.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = models.h; sourceTree = ""; }; + 7F0610290876ED46001EA95C /* modeltypes.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = modeltypes.h; sourceTree = ""; }; + 7F06102A0876ED46001EA95C /* music.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = music.cpp; sourceTree = ""; }; + 7F06102B0876ED46001EA95C /* music.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = music.h; sourceTree = ""; }; + 7F06102C0876ED46001EA95C /* network.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = network.h; sourceTree = ""; }; + 7F06102E0876ED46001EA95C /* network_NT.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = network_NT.cpp; sourceTree = ""; }; + 7F06102F0876ED46001EA95C /* networkphysics.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = networkphysics.cpp; sourceTree = ""; }; + 7F0610300876ED46001EA95C /* networkphysics.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = networkphysics.h; sourceTree = ""; }; + 7F0610310876ED46001EA95C /* parser.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = parser.cpp; sourceTree = ""; }; + 7F0610320876ED46001EA95C /* parser.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = parser.h; sourceTree = ""; }; + 7F0610330876ED46001EA95C /* particles.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = particles.cpp; sourceTree = ""; }; + 7F0610340876ED46001EA95C /* particles.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = particles.h; sourceTree = ""; }; + 7F0610350876ED46001EA95C /* random.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = random.cpp; sourceTree = ""; }; + 7F0610360876ED46001EA95C /* random.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = random.h; sourceTree = ""; }; + 7F0610370876ED46001EA95C /* rendercar.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = rendercar.cpp; sourceTree = ""; }; + 7F0610380876ED46001EA95C /* rendercar.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = rendercar.h; sourceTree = ""; }; + 7F0610390876ED46001EA95C /* renderframe.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = renderframe.cpp; sourceTree = ""; }; + 7F06103A0876ED46001EA95C /* renderframe.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = renderframe.h; sourceTree = ""; }; + 7F06103B0876ED46001EA95C /* roads.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = roads.cpp; sourceTree = ""; }; + 7F06103C0876ED46001EA95C /* roads.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = roads.h; sourceTree = ""; }; + 7F06103D0876ED46001EA95C /* screen.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = screen.cpp; sourceTree = ""; }; + 7F06103E0876ED46001EA95C /* screen.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = screen.h; sourceTree = ""; }; + 7F0610400876ED46001EA95C /* sky.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = sky.cpp; sourceTree = ""; }; + 7F0610410876ED46001EA95C /* sky.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = sky.h; sourceTree = ""; }; + 7F0610440876ED46001EA95C /* sound_ST.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = sound_ST.cpp; sourceTree = ""; }; + 7F0610450876ED46001EA95C /* stencil.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = stencil.cpp; sourceTree = ""; }; + 7F0610460876ED46001EA95C /* stencil.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = stencil.h; sourceTree = ""; }; + 7F0610490876ED46001EA95C /* text.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = text.cpp; sourceTree = ""; }; + 7F06104A0876ED46001EA95C /* text.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = text.h; sourceTree = ""; }; + 7F06104B0876ED46001EA95C /* textures.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = textures.cpp; sourceTree = ""; }; + 7F06104C0876ED46001EA95C /* textures.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = textures.h; sourceTree = ""; }; + 7F06104D0876ED46001EA95C /* texturesimport.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = texturesimport.h; sourceTree = ""; }; + 7F06104E0876ED46001EA95C /* tire.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = tire.cpp; sourceTree = ""; }; + 7F06104F0876ED46001EA95C /* tracker.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = tracker.cpp; sourceTree = ""; }; + 7F0610500876ED46001EA95C /* tracker.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = tracker.h; sourceTree = ""; }; + 7F0610510876ED46001EA95C /* tracks.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = tracks.cpp; sourceTree = ""; }; + 7F0610520876ED46001EA95C /* tracks.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = tracks.h; sourceTree = ""; }; + 7F0610530876ED46001EA95C /* transparency.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = transparency.cpp; sourceTree = ""; }; + 7F0610540876ED46001EA95C /* transparency.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = transparency.h; sourceTree = ""; }; + 7F0610550876ED46001EA95C /* vectors.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = vectors.cpp; sourceTree = ""; }; + 7F0610560876ED46001EA95C /* vectors.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = vectors.h; sourceTree = ""; }; + 7F0610570876ED46001EA95C /* writeout.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = writeout.cpp; sourceTree = ""; }; + 7F0610580876ED46001EA95C /* writeout.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = writeout.h; sourceTree = ""; }; + 7F06137C08770401001EA95C /* HID_Utilities_External.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = HID_Utilities_External.h; path = "/apps/dev/SDKs/HID Utilities Source/HID_Utilities_External.h"; sourceTree = ""; }; + 7F06138E08770577001EA95C /* interface.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = interface.cpp; sourceTree = ""; }; + 7F0613B708771262001EA95C /* compress.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = compress.h; path = ../packer/compress.h; sourceTree = SOURCE_ROOT; }; + 7F0613B808771263001EA95C /* LZRW.H */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = LZRW.H; path = ../packer/LZRW.H; sourceTree = SOURCE_ROOT; }; + 7F0613B908771263001EA95C /* LZRW3-A.C */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = "LZRW3-A.C"; path = "../packer/LZRW3-A.C"; sourceTree = SOURCE_ROOT; }; + 7F0613BA08771263001EA95C /* port.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = port.h; path = ../packer/port.h; sourceTree = SOURCE_ROOT; }; + 7F16E74E0D57A68700706C33 /* rt3_redline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = rt3_redline.h; path = /apps/dev/SDKs/ASW/ASWRegistration/redline/rt3_extras/rt3_redline.h; sourceTree = ""; }; + 7F16E75E0D57AA8200706C33 /* librt3_nonag.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; name = librt3_nonag.o; path = /apps/dev/SDKs/ASW/ASWRegistration/librt3_nonag.o; sourceTree = ""; }; + 7F16E7640D57ABCC00706C33 /* ASWRegistrationCarbonAPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASWRegistrationCarbonAPI.h; path = /apps/dev/SDKs/ASW/ASWRegistration/redline/CarbonAPI/ASWRegistrationCarbonAPI.h; sourceTree = ""; }; + 7F4348C5096D9B5B00C3981C /* GetPID.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = GetPID.c; sourceTree = ""; }; + 7F4348C6096D9B5B00C3981C /* GetPID.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = GetPID.h; sourceTree = ""; }; + 7F434A4C0973EC9900C3981C /* HID_cookie_strings.plist */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.xml; path = HID_cookie_strings.plist; sourceTree = ""; }; + 7F434A4D0973EC9900C3981C /* HID_device_usage_strings.plist */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.xml; path = HID_device_usage_strings.plist; sourceTree = ""; }; + 7F434A4E0973EC9900C3981C /* HID_usage_strings.plist */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.xml; path = HID_usage_strings.plist; sourceTree = ""; }; + 7F7EDC7F09EE95E000B2A665 /* Prev Track.scpt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.scpt; path = "Prev Track.scpt"; sourceTree = ""; }; + 7F7F050D09C9E5E3002D0EE3 /* interfacemultiplayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = interfacemultiplayer.h; sourceTree = ""; }; + 7F8C148C0A3ABBA000E76109 /* notifications.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = notifications.mm; sourceTree = ""; }; + 7F8C14A60A3ABDF900E76109 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; + 7F9E3C4F095331F7000394C1 /* Next Track.scpt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.scpt; path = "Next Track.scpt"; sourceTree = ""; }; + 7F9E3C7109534C98000394C1 /* PlayPause.scpt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.scpt; path = PlayPause.scpt; sourceTree = ""; }; + 7F9E3C7209534CA8000394C1 /* Status.scpt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.scpt; path = Status.scpt; sourceTree = ""; }; + 7FBFFFDE08ACA6BB00618F96 /* ImmrHIDUtilAddOn.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = ImmrHIDUtilAddOn.c; path = "/apps/dev/SDKs/FFB SDK/=>Developer=>Examples/ForceFeedback/UseFFAPIFromHIDUtil/ImmrHIDUtilAddOn.c"; sourceTree = ""; }; + 7FBFFFDF08ACA6BB00618F96 /* ImmrHIDUtilAddOn.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ImmrHIDUtilAddOn.h; path = "/apps/dev/SDKs/FFB SDK/=>Developer=>Examples/ForceFeedback/UseFFAPIFromHIDUtil/ImmrHIDUtilAddOn.h"; sourceTree = ""; }; + 7FC1DAFC087820860029047D /* AGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AGL.framework; path = /System/Library/Frameworks/AGL.framework; sourceTree = ""; }; + 7FC1DB0D087820970029047D /* QuickTime.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuickTime.framework; path = /System/Library/Frameworks/QuickTime.framework; sourceTree = ""; }; + 7FC1DB320878209F0029047D /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = /System/Library/Frameworks/OpenGL.framework; sourceTree = ""; }; + 7FC1DC3608782A5C0029047D /* ForceFeedback.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ForceFeedback.framework; path = /System/Library/Frameworks/ForceFeedback.framework; sourceTree = ""; }; + 7FC5FD440D49131300C76FF4 /* AmbrosiaTools.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AmbrosiaTools.framework; path = /apps/dev/SDKs/ASW/AmbrosiaTools/AmbrosiaTools.framework; sourceTree = ""; }; + 7FD85ABF08BC697A00C3EB17 /* Redline.rsrc */ = {isa = PBXFileReference; lastKnownFileType = archive.rsrc; path = Redline.rsrc; sourceTree = ""; }; + 7FD85B0508BC760A00C3EB17 /* S3Decompression.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = S3Decompression.cpp; sourceTree = ""; }; + 7FD85B0608BC760A00C3EB17 /* S3Decompression.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = S3Decompression.h; sourceTree = ""; }; + 7FD85D4108BC77B900C3EB17 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = /System/Library/Frameworks/IOKit.framework; sourceTree = ""; }; + 7FD85DEA08BC79EF00C3EB17 /* HID_Config_Utilities.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = HID_Config_Utilities.c; path = "/apps/dev/SDKs/HID Utilities Source/HID_Config_Utilities.c"; sourceTree = ""; }; + 7FD85DEB08BC79EF00C3EB17 /* HID_Error_Handler.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = HID_Error_Handler.c; path = "/apps/dev/SDKs/HID Utilities Source/HID_Error_Handler.c"; sourceTree = ""; }; + 7FD85DEC08BC79EF00C3EB17 /* HID_Name_Lookup.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = HID_Name_Lookup.c; path = "/apps/dev/SDKs/HID Utilities Source/HID_Name_Lookup.c"; sourceTree = ""; }; + 7FD85DED08BC79EF00C3EB17 /* HID_Queue_Utilities.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = HID_Queue_Utilities.c; path = "/apps/dev/SDKs/HID Utilities Source/HID_Queue_Utilities.c"; sourceTree = ""; }; + 7FD85DEE08BC79EF00C3EB17 /* HID_Transaction_Utilities.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = HID_Transaction_Utilities.c; path = "/apps/dev/SDKs/HID Utilities Source/HID_Transaction_Utilities.c"; sourceTree = ""; }; + 7FD85DEF08BC79EF00C3EB17 /* HID_Utilities.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = HID_Utilities.c; path = "/apps/dev/SDKs/HID Utilities Source/HID_Utilities.c"; sourceTree = ""; }; + 7FD85E2108BC9B8400C3EB17 /* fpu_exc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fpu_exc.h; sourceTree = ""; }; + 7FD85E2208BC9B8400C3EB17 /* fpu_exc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fpu_exc.c; sourceTree = ""; }; + 7FDFBE470A1BA49C0022488F /* libSystemStubs.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libSystemStubs.a; path = /usr/lib/libSystemStubs.a; sourceTree = ""; }; + 7FE3424509AF93CD006C8583 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = /System/Library/Frameworks/Security.framework; sourceTree = ""; }; + 7FE52CD10D579B8800F442A1 /* draw_tool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = draw_tool.h; path = /apps/dev/SDKs/ASW/AmbrosiaTools/draw_tool.h; sourceTree = ""; }; + 7FE52CD20D579B8800F442A1 /* file_tool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = file_tool.h; path = /apps/dev/SDKs/ASW/AmbrosiaTools/file_tool.h; sourceTree = ""; }; + 7FE52CD30D579B8800F442A1 /* getline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = getline.h; path = /apps/dev/SDKs/ASW/AmbrosiaTools/getline.h; sourceTree = ""; }; + 7FE52CD40D579B8800F442A1 /* inlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = inlines.h; path = /apps/dev/SDKs/ASW/AmbrosiaTools/inlines.h; sourceTree = ""; }; + 7FE52CD50D579B8800F442A1 /* interface_tool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = interface_tool.h; path = /apps/dev/SDKs/ASW/AmbrosiaTools/interface_tool.h; sourceTree = ""; }; + 7FE52CD60D579B8800F442A1 /* lsockets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = lsockets.h; path = /apps/dev/SDKs/ASW/AmbrosiaTools/lsockets.h; sourceTree = ""; }; + 7FE52CD70D579B8800F442A1 /* mac-carbon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "mac-carbon.h"; path = "/apps/dev/SDKs/ASW/AmbrosiaTools/mac-carbon.h"; sourceTree = ""; }; + 7FE52CD80D579B8800F442A1 /* mac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mac.h; path = /apps/dev/SDKs/ASW/AmbrosiaTools/mac.h; sourceTree = ""; }; + 7FE52CD90D579B8800F442A1 /* macdebug-carbon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "macdebug-carbon.h"; path = "/apps/dev/SDKs/ASW/AmbrosiaTools/macdebug-carbon.h"; sourceTree = ""; }; + 7FE52CDA0D579B8800F442A1 /* macdebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = macdebug.h; path = /apps/dev/SDKs/ASW/AmbrosiaTools/macdebug.h; sourceTree = ""; }; + 7FE52CDB0D579B8800F442A1 /* network_tool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = network_tool.h; path = /apps/dev/SDKs/ASW/AmbrosiaTools/network_tool.h; sourceTree = ""; }; + 7FE52CDC0D579B8800F442A1 /* platform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = platform.h; path = /apps/dev/SDKs/ASW/AmbrosiaTools/platform.h; sourceTree = ""; }; + 7FE52CDD0D579B8800F442A1 /* reg_tool_3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = reg_tool_3.h; path = /apps/dev/SDKs/ASW/AmbrosiaTools/reg_tool_3.h; sourceTree = ""; }; + 7FE52CDE0D579B8800F442A1 /* reggie.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = reggie.h; path = /apps/dev/SDKs/ASW/AmbrosiaTools/reggie.h; sourceTree = ""; }; + 7FE52CDF0D579B8800F442A1 /* sound_tool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = sound_tool.h; path = /apps/dev/SDKs/ASW/AmbrosiaTools/sound_tool.h; sourceTree = ""; }; + 7FE52CE00D579B8800F442A1 /* stddebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = stddebug.h; path = /apps/dev/SDKs/ASW/AmbrosiaTools/stddebug.h; sourceTree = ""; }; + 7FE52CE10D579B8800F442A1 /* stderror.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = stderror.h; path = /apps/dev/SDKs/ASW/AmbrosiaTools/stderror.h; sourceTree = ""; }; + 7FE52CE20D579B8800F442A1 /* stdmath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = stdmath.h; path = /apps/dev/SDKs/ASW/AmbrosiaTools/stdmath.h; sourceTree = ""; }; + 7FE52CE30D579B8800F442A1 /* stdtypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = stdtypes.h; path = /apps/dev/SDKs/ASW/AmbrosiaTools/stdtypes.h; sourceTree = ""; }; + 7FE52CE40D579B8800F442A1 /* unix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = unix.h; path = /apps/dev/SDKs/ASW/AmbrosiaTools/unix.h; sourceTree = ""; }; + 7FFFAA000886C5F70046AA19 /* Plugin.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = Plugin.icns; sourceTree = ""; }; + 7FFFAA010886C5F70046AA19 /* Redline.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = Redline.icns; sourceTree = ""; }; + 8D0C4E960486CD37000505A6 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; + 8D0C4E970486CD37000505A6 /* Redline.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Redline.app; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8D0C4E910486CD37000505A6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D0C4E920486CD37000505A6 /* Carbon.framework in Frameworks */, + 7FC1DAFD087820860029047D /* AGL.framework in Frameworks */, + 7FC1DB0E087820970029047D /* QuickTime.framework in Frameworks */, + 7FC1DB330878209F0029047D /* OpenGL.framework in Frameworks */, + 7FC1DC3708782A5C0029047D /* ForceFeedback.framework in Frameworks */, + 7FD85D4208BC77B900C3EB17 /* IOKit.framework in Frameworks */, + 7F55FFDB08DDDA9700B82C72 /* ApplicationServices.framework in Frameworks */, + 7F55FFDC08DDDA9800B82C72 /* CoreServices.framework in Frameworks */, + 7FE3424609AF93CD006C8583 /* Security.framework in Frameworks */, + 7FDFBE480A1BA49C0022488F /* libSystemStubs.a in Frameworks */, + 7F8C14A70A3ABDF900E76109 /* Cocoa.framework in Frameworks */, + 7FC5FD450D49131300C76FF4 /* AmbrosiaTools.framework in Frameworks */, + 7FED158B0D8FEA1F002AD94E /* librt3_nonag.o in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 195DF8CFFE9D517E11CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 8D0C4E970486CD37000505A6 /* Redline.app */, + ); + name = Products; + sourceTree = ""; + }; + 20286C29FDCF999611CA2CEA /* game */ = { + isa = PBXGroup; + children = ( + 7FE52CD00D579B7500F442A1 /* ASW */, + 7F0613AC08771244001EA95C /* lzrw */, + 7F06136E087703B4001EA95C /* HID */, + 7F060FE20876ED46001EA95C /* source */, + 20286C2CFDCF999611CA2CEA /* Resources */, + 20286C32FDCF999611CA2CEA /* External Frameworks and Libraries */, + 195DF8CFFE9D517E11CA2CBB /* Products */, + ); + name = game; + sourceTree = ""; + }; + 20286C2CFDCF999611CA2CEA /* Resources */ = { + isa = PBXGroup; + children = ( + 7F434A4C0973EC9900C3981C /* HID_cookie_strings.plist */, + 7F434A4D0973EC9900C3981C /* HID_device_usage_strings.plist */, + 7F434A4E0973EC9900C3981C /* HID_usage_strings.plist */, + 7F9E3C4F095331F7000394C1 /* Next Track.scpt */, + 7F7EDC7F09EE95E000B2A665 /* Prev Track.scpt */, + 7FD85ABF08BC697A00C3EB17 /* Redline.rsrc */, + 7FFFAA000886C5F70046AA19 /* Plugin.icns */, + 7FFFAA010886C5F70046AA19 /* Redline.icns */, + 7F9E3C7109534C98000394C1 /* PlayPause.scpt */, + 7F9E3C7209534CA8000394C1 /* Status.scpt */, + 8D0C4E960486CD37000505A6 /* Info.plist */, + 0867D6AAFE840B52C02AAC07 /* InfoPlist.strings */, + ); + name = Resources; + sourceTree = ""; + }; + 20286C32FDCF999611CA2CEA /* External Frameworks and Libraries */ = { + isa = PBXGroup; + children = ( + 7F8C14A60A3ABDF900E76109 /* Cocoa.framework */, + 7FDFBE470A1BA49C0022488F /* libSystemStubs.a */, + 7FE3424509AF93CD006C8583 /* Security.framework */, + 7FD85D4108BC77B900C3EB17 /* IOKit.framework */, + 7FC1DC3608782A5C0029047D /* ForceFeedback.framework */, + 7FC1DB320878209F0029047D /* OpenGL.framework */, + 7FC1DB0D087820970029047D /* QuickTime.framework */, + 7FC1DAFC087820860029047D /* AGL.framework */, + 20286C33FDCF999611CA2CEA /* Carbon.framework */, + 4A9504CAFFE6A41611CA0CBA /* CoreServices.framework */, + 4A9504C8FFE6A3BC11CA0CBA /* ApplicationServices.framework */, + ); + name = "External Frameworks and Libraries"; + sourceTree = ""; + }; + 7F060FE20876ED46001EA95C /* source */ = { + isa = PBXGroup; + children = ( + 7F4348C5096D9B5B00C3981C /* GetPID.c */, + 7F4348C6096D9B5B00C3981C /* GetPID.h */, + 7FD85E2108BC9B8400C3EB17 /* fpu_exc.h */, + 7FD85E2208BC9B8400C3EB17 /* fpu_exc.c */, + 7F060FE50876ED46001EA95C /* ai.cpp */, + 7F060FEA0876ED46001EA95C /* carphysics.cpp */, + 7F060FEB0876ED46001EA95C /* carphysics.h */, + 7F060FEC0876ED46001EA95C /* carselection.cpp */, + 7F060FF70876ED46001EA95C /* challenges.cpp */, + 7F060FF80876ED46001EA95C /* challenges.h */, + 7F060FF90876ED46001EA95C /* collision.cpp */, + 7F060FFA0876ED46001EA95C /* collision.h */, + 7F060FFB0876ED46001EA95C /* config.cpp */, + 7F060FED0876ED46001EA95C /* carselection.h */, + 7F060FFC0876ED46001EA95C /* config.h */, + 7F060FFD0876ED46001EA95C /* controls.cpp */, + 7F060FFE0876ED46001EA95C /* controls.h */, + 7F060FFF0876ED46001EA95C /* entities.cpp */, + 7F0610000876ED46001EA95C /* entities.h */, + 7F0610010876ED46001EA95C /* environment.cpp */, + 7F0610020876ED46001EA95C /* environment.h */, + 7F0610030876ED46001EA95C /* error.h */, + 7F0610040876ED46001EA95C /* fileio.cpp */, + 7F0610050876ED46001EA95C /* fileio.h */, + 7F0610060876ED46001EA95C /* gameframe.cpp */, + 7F0610070876ED46001EA95C /* gameframe.h */, + 7F0610080876ED46001EA95C /* gameinitexit.cpp */, + 7F0610090876ED46001EA95C /* gameinitexit.h */, + 7F06100A0876ED46001EA95C /* gamemem.h */, + 7F06100B0876ED46001EA95C /* gamesound.h */, + 7F06100C0876ED46001EA95C /* gamesystem.h */, + 7F06100D0876ED46001EA95C /* gametime.h */, + 7F06100F0876ED46001EA95C /* infodisplay.cpp */, + 7F0610100876ED46001EA95C /* initexit.cpp */, + 7F0610110876ED46001EA95C /* initexit.h */, + 7F06138E08770577001EA95C /* interface.cpp */, + 7F0610130876ED46001EA95C /* interface.h */, + 7F0610140876ED46001EA95C /* interfacemultiplayer.cpp */, + 7F7F050D09C9E5E3002D0EE3 /* interfacemultiplayer.h */, + 7F0610150876ED46001EA95C /* interfaceoptions.cpp */, + 7F0610160876ED46001EA95C /* interfaceutil.cpp */, + 7F0610170876ED46001EA95C /* interfaceutil.h */, + 7F0610180876ED46001EA95C /* lights.cpp */, + 7F0610190876ED46001EA95C /* lights.h */, + 7F06101A0876ED46001EA95C /* localtracker.h */, + 7F06101B0876ED46001EA95C /* log.cpp */, + 7F06101C0876ED46001EA95C /* log.h */, + 7F06101D0876ED46001EA95C /* maccontrols.cpp */, + 7F06101E0876ED46001EA95C /* macerror.cpp */, + 7F06101F0876ED46001EA95C /* macfileio.cpp */, + 7F0610200876ED46001EA95C /* maclocaltracker.cpp */, + 7F0610210876ED46001EA95C /* macscreen.cpp */, + 7F0610220876ED46001EA95C /* macsystem.cpp */, + 7F0610230876ED46001EA95C /* mactexturesimport.cpp */, + 7F0610240876ED46001EA95C /* main.cpp */, + 7F0610250876ED46001EA95C /* mapselection.cpp */, + 7F0610260876ED46001EA95C /* mapselection.h */, + 7F0610270876ED46001EA95C /* models.cpp */, + 7F0610280876ED46001EA95C /* models.h */, + 7F0610290876ED46001EA95C /* modeltypes.h */, + 7F06102A0876ED46001EA95C /* music.cpp */, + 7F06102B0876ED46001EA95C /* music.h */, + 7F06102C0876ED46001EA95C /* network.h */, + 7F06102E0876ED46001EA95C /* network_NT.cpp */, + 7F06102F0876ED46001EA95C /* networkphysics.cpp */, + 7F0610300876ED46001EA95C /* networkphysics.h */, + 7F8C148C0A3ABBA000E76109 /* notifications.mm */, + 7F0610310876ED46001EA95C /* parser.cpp */, + 7F0610320876ED46001EA95C /* parser.h */, + 7F0610330876ED46001EA95C /* particles.cpp */, + 7F0610340876ED46001EA95C /* particles.h */, + 7F0610350876ED46001EA95C /* random.cpp */, + 7F0610360876ED46001EA95C /* random.h */, + 7F0610370876ED46001EA95C /* rendercar.cpp */, + 7F0610380876ED46001EA95C /* rendercar.h */, + 7F0610390876ED46001EA95C /* renderframe.cpp */, + 7F06103A0876ED46001EA95C /* renderframe.h */, + 7F06103B0876ED46001EA95C /* roads.cpp */, + 7F06103C0876ED46001EA95C /* roads.h */, + 7FD85B0508BC760A00C3EB17 /* S3Decompression.cpp */, + 7FD85B0608BC760A00C3EB17 /* S3Decompression.h */, + 7F06103D0876ED46001EA95C /* screen.cpp */, + 7F06103E0876ED46001EA95C /* screen.h */, + 7F0610400876ED46001EA95C /* sky.cpp */, + 7F0610410876ED46001EA95C /* sky.h */, + 7F0610440876ED46001EA95C /* sound_ST.cpp */, + 7F0610450876ED46001EA95C /* stencil.cpp */, + 7F0610460876ED46001EA95C /* stencil.h */, + 7F0610490876ED46001EA95C /* text.cpp */, + 7F06104A0876ED46001EA95C /* text.h */, + 7F06104B0876ED46001EA95C /* textures.cpp */, + 7F06104C0876ED46001EA95C /* textures.h */, + 7F06104D0876ED46001EA95C /* texturesimport.h */, + 7F06104E0876ED46001EA95C /* tire.cpp */, + 7F06104F0876ED46001EA95C /* tracker.cpp */, + 7F0610500876ED46001EA95C /* tracker.h */, + 7F0610510876ED46001EA95C /* tracks.cpp */, + 7F0610520876ED46001EA95C /* tracks.h */, + 7F0610530876ED46001EA95C /* transparency.cpp */, + 7F0610540876ED46001EA95C /* transparency.h */, + 7F0610550876ED46001EA95C /* vectors.cpp */, + 7F0610560876ED46001EA95C /* vectors.h */, + 7F0610570876ED46001EA95C /* writeout.cpp */, + 7F0610580876ED46001EA95C /* writeout.h */, + ); + path = source; + sourceTree = ""; + }; + 7F06136E087703B4001EA95C /* HID */ = { + isa = PBXGroup; + children = ( + 7FD85DEA08BC79EF00C3EB17 /* HID_Config_Utilities.c */, + 7FD85DEB08BC79EF00C3EB17 /* HID_Error_Handler.c */, + 7FD85DEC08BC79EF00C3EB17 /* HID_Name_Lookup.c */, + 7FD85DED08BC79EF00C3EB17 /* HID_Queue_Utilities.c */, + 7FD85DEE08BC79EF00C3EB17 /* HID_Transaction_Utilities.c */, + 7FD85DEF08BC79EF00C3EB17 /* HID_Utilities.c */, + 7FBFFFDE08ACA6BB00618F96 /* ImmrHIDUtilAddOn.c */, + 7FBFFFDF08ACA6BB00618F96 /* ImmrHIDUtilAddOn.h */, + 7F06137C08770401001EA95C /* HID_Utilities_External.h */, + ); + name = HID; + sourceTree = ""; + }; + 7F0613AC08771244001EA95C /* lzrw */ = { + isa = PBXGroup; + children = ( + 7F0613B708771262001EA95C /* compress.h */, + 7F0613B808771263001EA95C /* LZRW.H */, + 7F0613B908771263001EA95C /* LZRW3-A.C */, + 7F0613BA08771263001EA95C /* port.h */, + ); + name = lzrw; + sourceTree = ""; + }; + 7FE52CD00D579B7500F442A1 /* ASW */ = { + isa = PBXGroup; + children = ( + 7F16E7640D57ABCC00706C33 /* ASWRegistrationCarbonAPI.h */, + 7F16E75E0D57AA8200706C33 /* librt3_nonag.o */, + 7F16E74E0D57A68700706C33 /* rt3_redline.h */, + 7FE52CD10D579B8800F442A1 /* draw_tool.h */, + 7FE52CD20D579B8800F442A1 /* file_tool.h */, + 7FE52CD30D579B8800F442A1 /* getline.h */, + 7FE52CD40D579B8800F442A1 /* inlines.h */, + 7FE52CD50D579B8800F442A1 /* interface_tool.h */, + 7FE52CD60D579B8800F442A1 /* lsockets.h */, + 7FE52CD70D579B8800F442A1 /* mac-carbon.h */, + 7FE52CD80D579B8800F442A1 /* mac.h */, + 7FE52CD90D579B8800F442A1 /* macdebug-carbon.h */, + 7FE52CDA0D579B8800F442A1 /* macdebug.h */, + 7FE52CDB0D579B8800F442A1 /* network_tool.h */, + 7FE52CDC0D579B8800F442A1 /* platform.h */, + 7FE52CDD0D579B8800F442A1 /* reg_tool_3.h */, + 7FE52CDE0D579B8800F442A1 /* reggie.h */, + 7FE52CDF0D579B8800F442A1 /* sound_tool.h */, + 7FE52CE00D579B8800F442A1 /* stddebug.h */, + 7FE52CE10D579B8800F442A1 /* stderror.h */, + 7FE52CE20D579B8800F442A1 /* stdmath.h */, + 7FE52CE30D579B8800F442A1 /* stdtypes.h */, + 7FE52CE40D579B8800F442A1 /* unix.h */, + 7FC5FD440D49131300C76FF4 /* AmbrosiaTools.framework */, + ); + name = ASW; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8D0C4E890486CD37000505A6 /* Redline */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7F060FD80876EC7F001EA95C /* Build configuration list for PBXNativeTarget "Redline" */; + buildPhases = ( + 8D0C4E8C0486CD37000505A6 /* Resources */, + 8D0C4E8F0486CD37000505A6 /* Sources */, + 8D0C4E910486CD37000505A6 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Redline; + productInstallPath = "$(HOME)/Applications"; + productName = game; + productReference = 8D0C4E970486CD37000505A6 /* Redline.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 20286C28FDCF999611CA2CEA /* Project object */ = { + isa = PBXProject; + buildConfigurationList = 7F060FDC0876EC7F001EA95C /* Build configuration list for PBXProject "game" */; + compatibilityVersion = "Xcode 2.4"; + hasScannedForEncodings = 1; + mainGroup = 20286C29FDCF999611CA2CEA /* game */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8D0C4E890486CD37000505A6 /* Redline */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8D0C4E8C0486CD37000505A6 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7F9E3C7409534CB5000394C1 /* PlayPause.scpt in Resources */, + 7F7EDC8309EE96BA00B2A665 /* Prev Track.scpt in Resources */, + 7F9E3C7309534CB2000394C1 /* Status.scpt in Resources */, + 8D0C4E8D0486CD37000505A6 /* InfoPlist.strings in Resources */, + 7FFFAA020886C5F70046AA19 /* Plugin.icns in Resources */, + 7F9E3C5B095334BF000394C1 /* Next Track.scpt in Resources */, + 7FFFAA030886C5F70046AA19 /* Redline.icns in Resources */, + 7FD85AC008BC697A00C3EB17 /* Redline.rsrc in Resources */, + 7F434A4F0973EC9900C3981C /* HID_cookie_strings.plist in Resources */, + 7F434A500973EC9900C3981C /* HID_device_usage_strings.plist in Resources */, + 7F434A510973EC9900C3981C /* HID_usage_strings.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8D0C4E8F0486CD37000505A6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7F06105A0876ED46001EA95C /* ai.cpp in Sources */, + 7F06105D0876ED46001EA95C /* carphysics.cpp in Sources */, + 7F06105E0876ED46001EA95C /* carselection.cpp in Sources */, + 7F0610640876ED46001EA95C /* collision.cpp in Sources */, + 7F0610650876ED46001EA95C /* config.cpp in Sources */, + 7F0610660876ED46001EA95C /* controls.cpp in Sources */, + 7F0610670876ED46001EA95C /* entities.cpp in Sources */, + 7F0610680876ED46001EA95C /* environment.cpp in Sources */, + 7F0610690876ED46001EA95C /* fileio.cpp in Sources */, + 7F06106A0876ED46001EA95C /* gameframe.cpp in Sources */, + 7F06106B0876ED46001EA95C /* gameinitexit.cpp in Sources */, + 7F06106D0876ED46001EA95C /* infodisplay.cpp in Sources */, + 7F06106E0876ED46001EA95C /* initexit.cpp in Sources */, + 7F0610700876ED46001EA95C /* interfacemultiplayer.cpp in Sources */, + 7F0610710876ED46001EA95C /* interfaceoptions.cpp in Sources */, + 7F0610720876ED46001EA95C /* interfaceutil.cpp in Sources */, + 7F0610730876ED46001EA95C /* lights.cpp in Sources */, + 7F0610740876ED46001EA95C /* log.cpp in Sources */, + 7F0610750876ED46001EA95C /* maccontrols.cpp in Sources */, + 7F0610760876ED46001EA95C /* macerror.cpp in Sources */, + 7F0610770876ED46001EA95C /* macfileio.cpp in Sources */, + 7F0610780876ED46001EA95C /* maclocaltracker.cpp in Sources */, + 7F0610790876ED46001EA95C /* macscreen.cpp in Sources */, + 7F06107A0876ED46001EA95C /* macsystem.cpp in Sources */, + 7F06107B0876ED46001EA95C /* mactexturesimport.cpp in Sources */, + 7F06107C0876ED46001EA95C /* main.cpp in Sources */, + 7F06107D0876ED46001EA95C /* mapselection.cpp in Sources */, + 7F06107E0876ED46001EA95C /* models.cpp in Sources */, + 7F06107F0876ED46001EA95C /* music.cpp in Sources */, + 7F0610810876ED46001EA95C /* network_NT.cpp in Sources */, + 7F0610820876ED46001EA95C /* networkphysics.cpp in Sources */, + 7F0610830876ED46001EA95C /* parser.cpp in Sources */, + 7F0610840876ED46001EA95C /* particles.cpp in Sources */, + 7F0610850876ED46001EA95C /* random.cpp in Sources */, + 7F0610860876ED46001EA95C /* rendercar.cpp in Sources */, + 7F0610870876ED46001EA95C /* renderframe.cpp in Sources */, + 7F0610880876ED46001EA95C /* roads.cpp in Sources */, + 7F0610890876ED46001EA95C /* screen.cpp in Sources */, + 7F06108A0876ED46001EA95C /* sky.cpp in Sources */, + 7F06108D0876ED46001EA95C /* sound_ST.cpp in Sources */, + 7F06108E0876ED46001EA95C /* stencil.cpp in Sources */, + 7F0610900876ED46001EA95C /* text.cpp in Sources */, + 7F0610910876ED46001EA95C /* textures.cpp in Sources */, + 7F0610920876ED46001EA95C /* tire.cpp in Sources */, + 7F0610930876ED46001EA95C /* tracker.cpp in Sources */, + 7F0610940876ED46001EA95C /* tracks.cpp in Sources */, + 7F0610950876ED46001EA95C /* transparency.cpp in Sources */, + 7F0610960876ED46001EA95C /* vectors.cpp in Sources */, + 7F0610970876ED46001EA95C /* writeout.cpp in Sources */, + 7F06138F08770577001EA95C /* interface.cpp in Sources */, + 7F0613BB08771263001EA95C /* LZRW3-A.C in Sources */, + 7FBFFFE008ACA6BB00618F96 /* ImmrHIDUtilAddOn.c in Sources */, + 7FD85B0708BC760A00C3EB17 /* S3Decompression.cpp in Sources */, + 7FD85DF008BC79EF00C3EB17 /* HID_Config_Utilities.c in Sources */, + 7FD85DF108BC79EF00C3EB17 /* HID_Error_Handler.c in Sources */, + 7FD85DF208BC79EF00C3EB17 /* HID_Name_Lookup.c in Sources */, + 7FD85DF308BC79EF00C3EB17 /* HID_Queue_Utilities.c in Sources */, + 7FD85DF408BC79EF00C3EB17 /* HID_Transaction_Utilities.c in Sources */, + 7FD85DF508BC79EF00C3EB17 /* HID_Utilities.c in Sources */, + 7FD85E2308BC9B8400C3EB17 /* fpu_exc.c in Sources */, + 7F4348C7096D9B5B00C3981C /* GetPID.c in Sources */, + 7F8C148D0A3ABBA000E76109 /* notifications.mm in Sources */, + 7F6EB7060DBB0141008B14A9 /* challenges.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 0867D6AAFE840B52C02AAC07 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 0867D6ABFE840B52C02AAC07 /* English */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 7F060FD90876EC7F001EA95C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + /apps/dev/SDKs/ASW/AmbrosiaTools, + /apps/dev/SDKs/ASW/ASWRegistration/redline/Framework/Release, + ); + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = game_Prefix.pch; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "$(HOME)/Applications"; + LIBRARY_SEARCH_PATHS = ( + "/apps/dev/SDKs/Ambrosia-CW-02-26-2005/network_tool/libraries", + "/apps/dev/SDKs/Ambrosia-CW-02-26-2005/reg_tool_3/libraries", + "/apps/dev/SDKs/Ambrosia-CW-02-26-2005/reggie/libraries", + "/apps/dev/SDKs/Ambrosia-CW-02-26-2005/sound_tool/libraries", + "/apps/dev/SDKs/Ambrosia-gcc-02-26-2005/lsockets", + "/apps/dev/SDKs/Ambrosia-gcc-02-26-2005/network_tool", + "/apps/dev/SDKs/Ambrosia-gcc-02-26-2005/platform", + "/apps/dev/SDKs/Ambrosia-gcc-02-26-2005/reg_tool_3", + "/apps/dev/SDKs/Ambrosia-gcc-02-26-2005/reggie", + "/apps/dev/SDKs/Ambrosia-gcc-02-26-2005/sound_tool", + "/apps/dev/SDKs/Ambrosia-gcc-02-26-2005/file_tool", + "/apps/dev/SDKs/Ambrosia-gcc-02-26-2005/interface_tool", + "/apps/dev/SDKs/Ambrosia-gcc-02-26-2005/draw_tool", + "$(LIBRARY_SEARCH_PATHS_QUOTED_1)", + ); + LIBRARY_SEARCH_PATHS_QUOTED_1 = "/apps/dev/SDKs/HID\\ Utilities\\ Source/build"; + PREBINDING = NO; + PRODUCT_NAME = Redline; + WRAPPER_EXTENSION = app; + ZERO_LINK = NO; + }; + name = Debug; + }; + 7F060FDA0876EC7F001EA95C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + /apps/dev/SDKs/ASW/AmbrosiaTools, + /apps/dev/SDKs/ASW/ASWRegistration/redline/Framework/Release, + ); + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = game_Prefix.pch; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "$(HOME)/Applications"; + LIBRARY_SEARCH_PATHS = ( + "/apps/dev/SDKs/Ambrosia-CW-02-26-2005/network_tool/libraries", + "/apps/dev/SDKs/Ambrosia-CW-02-26-2005/reg_tool_3/libraries", + "/apps/dev/SDKs/Ambrosia-CW-02-26-2005/reggie/libraries", + "/apps/dev/SDKs/Ambrosia-CW-02-26-2005/sound_tool/libraries", + "/apps/dev/SDKs/Ambrosia-gcc-02-26-2005/lsockets", + "/apps/dev/SDKs/Ambrosia-gcc-02-26-2005/network_tool", + "/apps/dev/SDKs/Ambrosia-gcc-02-26-2005/platform", + "/apps/dev/SDKs/Ambrosia-gcc-02-26-2005/reg_tool_3", + "/apps/dev/SDKs/Ambrosia-gcc-02-26-2005/reggie", + "/apps/dev/SDKs/Ambrosia-gcc-02-26-2005/sound_tool", + "/apps/dev/SDKs/Ambrosia-gcc-02-26-2005/file_tool", + "/apps/dev/SDKs/Ambrosia-gcc-02-26-2005/interface_tool", + "/apps/dev/SDKs/Ambrosia-gcc-02-26-2005/draw_tool", + "$(LIBRARY_SEARCH_PATHS_QUOTED_1)", + ); + LIBRARY_SEARCH_PATHS_QUOTED_1 = "/apps/dev/SDKs/HID\\ Utilities\\ Source/build"; + PREBINDING = NO; + PRODUCT_NAME = Redline; + WRAPPER_EXTENSION = app; + ZERO_LINK = NO; + }; + name = Release; + }; + 7F060FDB0876EC7F001EA95C /* Default */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + /apps/dev/SDKs/ASW/AmbrosiaTools, + /apps/dev/SDKs/ASW/ASWRegistration/redline/Framework/Release, + ); + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = game_Prefix.pch; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "$(HOME)/Applications"; + LIBRARY_SEARCH_PATHS = ( + "/apps/dev/SDKs/Ambrosia-CW-02-26-2005/network_tool/libraries", + "/apps/dev/SDKs/Ambrosia-CW-02-26-2005/reg_tool_3/libraries", + "/apps/dev/SDKs/Ambrosia-CW-02-26-2005/reggie/libraries", + "/apps/dev/SDKs/Ambrosia-CW-02-26-2005/sound_tool/libraries", + "/apps/dev/SDKs/Ambrosia-gcc-02-26-2005/lsockets", + "/apps/dev/SDKs/Ambrosia-gcc-02-26-2005/network_tool", + "/apps/dev/SDKs/Ambrosia-gcc-02-26-2005/platform", + "/apps/dev/SDKs/Ambrosia-gcc-02-26-2005/reg_tool_3", + "/apps/dev/SDKs/Ambrosia-gcc-02-26-2005/reggie", + "/apps/dev/SDKs/Ambrosia-gcc-02-26-2005/sound_tool", + "/apps/dev/SDKs/Ambrosia-gcc-02-26-2005/file_tool", + "/apps/dev/SDKs/Ambrosia-gcc-02-26-2005/interface_tool", + "/apps/dev/SDKs/Ambrosia-gcc-02-26-2005/draw_tool", + "$(LIBRARY_SEARCH_PATHS_QUOTED_1)", + ); + LIBRARY_SEARCH_PATHS_QUOTED_1 = "/apps/dev/SDKs/HID\\ Utilities\\ Source/build"; + PREBINDING = NO; + PRODUCT_NAME = Redline; + WRAPPER_EXTENSION = app; + }; + name = Default; + }; + 7F060FDD0876EC7F001EA95C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = ppc; + MACOSX_DEPLOYMENT_TARGET = 10.4; + OTHER_LDFLAGS = "-lz"; + SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; + ZERO_LINK = NO; + }; + name = Debug; + }; + 7F060FDE0876EC7F001EA95C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1)"; + ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1 = "ppc i386"; + DEAD_CODE_STRIPPING = YES; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_VERSION_i386 = 4.0; + GCC_VERSION_ppc = 3.3; + HEADER_SEARCH_PATHS = ""; + MACOSX_DEPLOYMENT_TARGET = 10.2; + MACOSX_DEPLOYMENT_TARGET_i386 = 10.4; + MACOSX_DEPLOYMENT_TARGET_ppc = 10.2; + ONLY_ACTIVE_ARCH = NO; + OTHER_LDFLAGS = ( + "-lz", + "-weak_library", + $SDKROOT/usr/lib/libcurl.dylib, + ); + SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; + SDKROOT_i386 = /Developer/SDKs/MacOSX10.4u.sdk; + SDKROOT_ppc = /Developer/SDKs/MacOSX10.3.9.sdk; + }; + name = Release; + }; + 7F060FDF0876EC7F001EA95C /* Default */ = { + isa = XCBuildConfiguration; + buildSettings = { + MACOSX_DEPLOYMENT_TARGET = 10.4; + SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; + }; + name = Default; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 7F060FD80876EC7F001EA95C /* Build configuration list for PBXNativeTarget "Redline" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7F060FD90876EC7F001EA95C /* Debug */, + 7F060FDA0876EC7F001EA95C /* Release */, + 7F060FDB0876EC7F001EA95C /* Default */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Default; + }; + 7F060FDC0876EC7F001EA95C /* Build configuration list for PBXProject "game" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7F060FDD0876EC7F001EA95C /* Debug */, + 7F060FDE0876EC7F001EA95C /* Release */, + 7F060FDF0876EC7F001EA95C /* Default */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Default; + }; +/* End XCConfigurationList section */ + }; + rootObject = 20286C28FDCF999611CA2CEA /* Project object */; +} diff --git a/game_Prefix.pch b/game_Prefix.pch new file mode 100644 index 0000000..4431c58 --- /dev/null +++ b/game_Prefix.pch @@ -0,0 +1,6 @@ +// +// Prefix header for all source files of the 'game' target in the 'game' project. +// + +#include +#include "mac-carbon.h" \ No newline at end of file diff --git a/iChat Status.scpt b/iChat Status.scpt new file mode 100644 index 0000000..61ad834 Binary files /dev/null and b/iChat Status.scpt differ diff --git a/source.zip b/source.zip new file mode 100644 index 0000000..c0db0dc Binary files /dev/null and b/source.zip differ diff --git a/source/GetPID.c b/source/GetPID.c new file mode 100755 index 0000000..3397274 --- /dev/null +++ b/source/GetPID.c @@ -0,0 +1,402 @@ +/* + File: GetPID.c + + Description: This file provides a simple API to do process PID lookup based on process name. + + Author: Chad Jones + + Copyright: © Copyright 2003 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Change History (most recent first): +*/ + +#include "GetPID.h" + +#include +#include +#include + +/***************************************************** + * GetAllPIDsForProcessName + ***************************************************** + * Purpose: This functions purpose is to lookup a BSD + * process PID given the BSD process name. This function may + * potentially return multiple PIDs for a given BSD process name + * since several processes can have the same BSD process name. + * + * Parameters: + * ProcessName A constant C-string. On calling + * GetAllPIDsForProcessName this variable holds the BSD process name + * used to do the process lookup. Note that the process name you need + * to pass is the name of the BSD executable process. If trying + * to find the PID of an regular OSX application you will need to pass the + * name of the actual BSD executable inside an application bundle (rather + * than the bundle name itself). In any case as a user you can find the + * BSD process name of any process (including OSX applications) by + * typing the command "ps -axcocommand,pid" in terminal. + * + * ArrayOfReturnedPIDs A pointer to a pre-allocated array of pid_t. + * On calling GetAllPIDsForProcessName this variable must be a pointer to a + * pre-allocated array of pid_t whos length (in number of pid_t entries) is defined + * in ArrayOfPIDsLength. On successful return from GetAllPIDsForProcessName + * this array will hold the PIDs of all processes which have a matching process + * name to that specified in the ProcessName input variable. The number of actual + * PIDs entered in the array starting at index zero will be the value returned + * in NumberOfMatchesFound. On failed return if the error is a buffer overflow + * error then the buffer will be filled to the max with PIDs which matched. + * Otherwise on failed return the state of the array will be undefined. Note + * the returned PID array is not sorted and is listed in order of process encountered. + * + * NumberOfPossiblePIDsInArray A unsigned integer. On calling + * GetAllPIDsForProcessName this variable will hold the number of + * pre-allocated PID entries which are in the ArrayOfReturnedPIDs for this functions + * use. Note this value must have a value greater than zero. + * + * NumberOfMatchesFound An unsigned integer. On calling GetAllPIDsForProcessName + * this variable will point to a pre-allocated unsigned integer. On return from + * GetAllPIDsForProcessName this variable will contain the number of PIDs contained in the + * ArrayOfReturnedPIDs. On failed return the value of the variable will be undefined. + * + * SysctlError A pointer to a pre-allocated integer. On failed return, this + * variable represents the error returned from the sysctl command. On function + * success this variable will have a value specified by the sysctl based on the + * error that occurred. On success the variable will have the value zero. + * Note this variable can also be NULL in which case the variable is ignored. + * + * *Function Result* A integer return value. + * See result codes listed below. + * Result Codes: + * 0 Success. A set of process PIDs were found and are located in + * ArrayOfReturnedPIDs array. + * -1 Could not find a process with a matching process name + * (i.e. process not found). + * -2 Invalid arguments passed. + * -3 Unable to get the size of sysctl buffer required + * (consult SysctlError return value for more information) + * -4 Unable to allocate memory to store BSD process information + * (consult SysctlError return value for more information) + * -5 The array passed to hold the returned PIDs is not large enough + * to hold all PIDs of process with matching names. + * + *****************************************************/ +int GetAllPIDsForProcessName(const char* ProcessName, + pid_t ArrayOfReturnedPIDs[], + const unsigned int NumberOfPossiblePIDsInArray, + unsigned int* NumberOfMatchesFound, + int* SysctlError) +{ + // --- Defining local variables for this function and initializing all to zero --- // + int mib[6] = {0,0,0,0,0,0}; //used for sysctl call. + int SuccessfullyGotProcessInformation; + size_t sizeOfBufferRequired = 0; //set to zero to start with. + int error = 0; + long NumberOfRunningProcesses = 0; + unsigned int Counter = 0; + struct kinfo_proc* BSDProcessInformationStructure = NULL; + pid_t CurrentExaminedProcessPID = 0; + char* CurrentExaminedProcessName = NULL; + + // --- Checking input arguments for validity --- // + if (ProcessName == NULL) //need valid process name + { + return(kInvalidArgumentsError); + } + + if (ArrayOfReturnedPIDs == NULL) //need an actual array + { + return(kInvalidArgumentsError); + } + + if (NumberOfPossiblePIDsInArray <= 0) + { + //length of the array must be larger than zero. + return(kInvalidArgumentsError); + } + + if (NumberOfMatchesFound == NULL) //need an integer for return. + { + return(kInvalidArgumentsError); + } + + + //--- Setting return values to known values --- // + + //initalizing PID array so all values are zero + memset(ArrayOfReturnedPIDs, 0, NumberOfPossiblePIDsInArray * sizeof(pid_t)); + + *NumberOfMatchesFound = 0; //no matches found yet + + if (SysctlError != NULL) //only set sysctlError if it is present + { + *SysctlError = 0; + } + + //--- Getting list of process information for all processes --- // + + /* Setting up the mib (Management Information Base) which is an array of integers where each + * integer specifies how the data will be gathered. Here we are setting the MIB + * block to lookup the information on all the BSD processes on the system. Also note that + * every regular application has a recognized BSD process accociated with it. We pass + * CTL_KERN, KERN_PROC, KERN_PROC_ALL to sysctl as the MIB to get back a BSD structure with + * all BSD process information for all processes in it (including BSD process names) + */ + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_ALL; + + /* Here we have a loop set up where we keep calling sysctl until we finally get an unrecoverable error + * (and we return) or we finally get a succesful result. Note with how dynamic the process list can + * be you can expect to have a failure here and there since the process list can change between + * getting the size of buffer required and the actually filling that buffer. + */ + SuccessfullyGotProcessInformation = FALSE; + + while (SuccessfullyGotProcessInformation == FALSE) + { + /* Now that we have the MIB for looking up process information we will pass it to sysctl to get the + * information we want on BSD processes. However, before we do this we must know the size of the buffer to + * allocate to accomidate the return value. We can get the size of the data to allocate also using the + * sysctl command. In this case we call sysctl with the proper arguments but specify no return buffer + * specified (null buffer). This is a special case which causes sysctl to return the size of buffer required. + * + * First Argument: The MIB which is really just an array of integers. Each integer is a constant + * representing what information to gather from the system. Check out the man page to know what + * constants sysctl will work with. Here of course we pass our MIB block which was passed to us. + * Second Argument: The number of constants in the MIB (array of integers). In this case there are three. + * Third Argument: The output buffer where the return value from sysctl will be stored. In this case + * we don't want anything return yet since we don't yet know the size of buffer needed. Thus we will + * pass null for the buffer to begin with. + * Forth Argument: The size of the output buffer required. Since the buffer itself is null we can just + * get the buffer size needed back from this call. + * Fifth Argument: The new value we want the system data to have. Here we don't want to set any system + * information we only want to gather it. Thus, we pass null as the buffer so sysctl knows that + * we have no desire to set the value. + * Sixth Argument: The length of the buffer containing new information (argument five). In this case + * argument five was null since we didn't want to set the system value. Thus, the size of the buffer + * is zero or NULL. + * Return Value: a return value indicating success or failure. Actually, sysctl will either return + * zero on no error and -1 on error. The errno UNIX variable will be set on error. + */ + error = sysctl(mib, 3, NULL, &sizeOfBufferRequired, NULL, NULL); + + /* If an error occurred then return the accociated error. The error itself actually is stored in the UNIX + * errno variable. We can access the errno value using the errno global variable. We will return the + * errno value as the sysctlError return value from this function. + */ + if (error != 0) + { + if (SysctlError != NULL) + { + *SysctlError = errno; //we only set this variable if the pre-allocated variable is given + } + + return(kErrorGettingSizeOfBufferRequired); + } + + /* Now we successful obtained the size of the buffer required for the sysctl call. This is stored in the + * SizeOfBufferRequired variable. We will malloc a buffer of that size to hold the sysctl result. + */ + BSDProcessInformationStructure = (struct kinfo_proc*) malloc(sizeOfBufferRequired); + + if (BSDProcessInformationStructure == NULL) + { + if (SysctlError != NULL) + { + *SysctlError = ENOMEM; //we only set this variable if the pre-allocated variable is given + } + + return(kUnableToAllocateMemoryForBuffer); //unrecoverable error (no memory available) so give up + } + + /* Now we have the buffer of the correct size to hold the result we can now call sysctl + * and get the process information. + * + * First Argument: The MIB for gathering information on running BSD processes. The MIB is really + * just an array of integers. Each integer is a constant representing what information to + * gather from the system. Check out the man page to know what constants sysctl will work with. + * Second Argument: The number of constants in the MIB (array of integers). In this case there are three. + * Third Argument: The output buffer where the return value from sysctl will be stored. This is the buffer + * which we allocated specifically for this purpose. + * Forth Argument: The size of the output buffer (argument three). In this case its the size of the + * buffer we already allocated. + * Fifth Argument: The buffer containing the value to set the system value to. In this case we don't + * want to set any system information we only want to gather it. Thus, we pass null as the buffer + * so sysctl knows that we have no desire to set the value. + * Sixth Argument: The length of the buffer containing new information (argument five). In this case + * argument five was null since we didn't want to set the system value. Thus, the size of the buffer + * is zero or NULL. + * Return Value: a return value indicating success or failure. Actually, sysctl will either return + * zero on no error and -1 on error. The errno UNIX variable will be set on error. + */ + error = sysctl(mib, 3, BSDProcessInformationStructure, &sizeOfBufferRequired, NULL, NULL); + + //Here we successfully got the process information. Thus set the variable to end this sysctl calling loop + if (error == 0) + { + SuccessfullyGotProcessInformation = TRUE; + } + else + { + /* failed getting process information we will try again next time around the loop. Note this is caused + * by the fact the process list changed between getting the size of the buffer and actually filling + * the buffer (something which will happen from time to time since the process list is dynamic). + * Anyways, the attempted sysctl call failed. We will now begin again by freeing up the allocated + * buffer and starting again at the beginning of the loop. + */ + free(BSDProcessInformationStructure); + } + }//end while loop + + // --- Going through process list looking for processes with matching names --- // + + /* Now that we have the BSD structure describing the running processes we will parse it for the desired + * process name. First we will the number of running processes. We can determine + * the number of processes running because there is a kinfo_proc structure for each process. + */ + NumberOfRunningProcesses = sizeOfBufferRequired / sizeof(struct kinfo_proc); + + /* Now we will go through each process description checking to see if the process name matches that + * passed to us. The BSDProcessInformationStructure has an array of kinfo_procs. Each kinfo_proc has + * an extern_proc accociated with it in the kp_proc attribute. Each extern_proc (kp_proc) has the process name + * of the process accociated with it in the p_comm attribute and the PID of that process in the p_pid attibute. + * We test the process name by compairing the process name passed to us with the value in the p_comm value. + * Note we limit the compairison to MAXCOMLEN which is the maximum length of a BSD process name which is used + * by the system. + */ + for (Counter = 0 ; Counter < NumberOfRunningProcesses ; Counter++) + { + //Getting PID of process we are examining + CurrentExaminedProcessPID = BSDProcessInformationStructure[Counter].kp_proc.p_pid; + + //Getting name of process we are examining + CurrentExaminedProcessName = BSDProcessInformationStructure[Counter].kp_proc.p_comm; + + if ((CurrentExaminedProcessPID > 0) //Valid PID + && ((strncmp(CurrentExaminedProcessName, ProcessName, MAXCOMLEN) == 0))) //name matches + { + // --- Got a match add it to the array if possible --- // + if ((*NumberOfMatchesFound + 1) > NumberOfPossiblePIDsInArray) + { + //if we overran the array buffer passed we release the allocated buffer give an error. + free(BSDProcessInformationStructure); + return(kPIDBufferOverrunError); + } + + //adding the value to the array. + ArrayOfReturnedPIDs[*NumberOfMatchesFound] = CurrentExaminedProcessPID; + + //incrementing our number of matches found. + *NumberOfMatchesFound = *NumberOfMatchesFound + 1; + } + }//end looking through process list + + free(BSDProcessInformationStructure); //done with allocated buffer so release. + + if (*NumberOfMatchesFound == 0) + { + //didn't find any matches return error. + return(kCouldNotFindRequestedProcess); + } + else + { + //found matches return success. + return(kSuccess); + } +} + +/***************************************************** + * GetPIDForProcessName + ***************************************************** + * Purpose: A convience call for GetAllPIDsForProcessName(). + * This function looks up a process PID given a BSD process + * name. + * + * Parameters: + * ProcessName A constant C-string. On calling + * GetPIDForProcessName this variable holds the BSD process name + * used to do the process lookup. Note that the process name you need + * to pass is the name of the BSD executable process. If trying + * to find the PID of an regular OSX application you will need to pass the + * name of the actual BSD executable inside an application bundle (rather + * than the bundle name itself). In any case as a user you can find the + * BSD process name of any process (including OSX applications) by + * typing the command "ps -axcocommand,pid" in terminal. + * + * *Function Result* A integer return value. + * See result codes listed below. + * Result Codes: + * >0 Success. The value returned is the PID of the + * matching process. + * -1 Error getting PID for requested process. This error can + * be caused by several things. One is if no such process exists. + * Another is if more than one process has the given name. The + * thing to do here is to call GetAllPIDsForProcessName() + * for complete error code or to get PIDs if there are multiple + * processes with that name. + *****************************************************/ +int GetPIDForProcessName(const char* ProcessName) +{ + pid_t PIDArray[1] = {0}; + int Error = 0; + int NumberOfMatches = 0; + + /* Here we are calling the function GetAllPIDsForProcessName which wil give us the PIDs + * of the process name we pass. Of course here we are hoping for a single PID return. + * First Argument: The BSD process name of the process we want to lookup. In this case the + * the process name passed to us. + * Second Argument: A preallocated array of pid_t. This is where the PIDs of matching processes + * will be placed on return. We pass the array we just allocated which is length one. + * Third Argument: The number of pid_t entries located in the array of pid_t (argument 2). In this + * case our array has one pid_t entry so pass one. + * Forth Argument: On return this will hold the number of PIDs placed into the + * pid_t array (array passed in argument 2). + * Fifth Argument: Passing NULL to ignore this argument. + * Return Value: An error indicating success (zero result) or failure (non-zero). + * + */ + Error = GetAllPIDsForProcessName(ProcessName, PIDArray, 1, &NumberOfMatches, NULL); + + if ((Error == 0) && (NumberOfMatches == 1))//success! + { + return((int) PIDArray[0]); //return the one PID we found. + } + else + { + return(-1); + } +} diff --git a/source/GetPID.h b/source/GetPID.h new file mode 100755 index 0000000..9450b27 --- /dev/null +++ b/source/GetPID.h @@ -0,0 +1,169 @@ +/* + File: GetPID.h + + Description: This file defines a simple API to do process PID lookup based on process name. + + Author: Chad Jones + + Copyright: © Copyright 2003 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Change History (most recent first): +*/ +#if !defined(__DTSSampleCode_GetPID__) +#define __DTSSampleCode_GetPID__ 1 + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +// --- Defining constants for use with sample code --- // +enum {kSuccess = 0, + kCouldNotFindRequestedProcess = -1, + kInvalidArgumentsError = -2, + kErrorGettingSizeOfBufferRequired = -3, + kUnableToAllocateMemoryForBuffer = -4, + kPIDBufferOverrunError = -5}; + +/***************************************************** + * GetAllPIDsForProcessName + ***************************************************** + * Purpose: This functions purpose is to lookup a BSD + * process PID given the BSD process name. This function may + * potentially return multiple PIDs for a given BSD process name + * since several processes can have the same BSD process name. + * + * Parameters: + * ProcessName A constant C-string. On calling + * GetAllPIDsForProcessName this variable holds the BSD process name + * used to do the process lookup. Note that the process name you need + * to pass is the name of the BSD executable process. If trying + * to find the PID of an regular OSX application you will need to pass the + * name of the actual BSD executable inside an application bundle (rather + * than the bundle name itself). In any case as a user you can find the + * BSD process name of any process (including OSX applications) by + * typing the command "ps -axcocommand,pid" in terminal. + * + * ArrayOfReturnedPIDs A pointer to a pre-allocated array of pid_t. + * On calling GetAllPIDsForProcessName this variable must be a pointer to a + * pre-allocated array of pid_t whos length (in number of pid_t entries) is defined + * in ArrayOfPIDsLength. On successful return from GetAllPIDsForProcessName + * this array will hold the PIDs of all processes which have a matching process + * name to that specified in the ProcessName input variable. The number of actual + * PIDs entered in the array starting at index zero will be the value returned + * in NumberOfMatchesFound. On failed return if the error is a buffer overflow + * error then the buffer will be filled to the max with PIDs which matched. + * Otherwise on failed return the state of the array will be undefined. + * + * NumberOfPossiblePIDsInArray A unsigned integer. On calling + * GetAllPIDsForProcessName this variable will hold the number of + * pre-allocated PID entries which are in the ArrayOfReturnedPIDs for this functions + * use. Note this value must have a value greater than zero. + * + * NumberOfMatchesFound An unsigned integer. On calling GetAllPIDsForProcessName + * this variable will point to a pre-allocated unsigned integer. On return from + * GetAllPIDsForProcessName this variable will contain the number of PIDs contained in the + * ArrayOfReturnedPIDs. On failed return the value of the variable will be undefined. + * + * SysctlError A pointer to a pre-allocated integer. On failed return, this + * variable represents the error returned from the sysctl command. On function + * success this variable will have a value specified by the sysctl based on the + * error that occurred. On success the variable will have the value zero. + * Note this variable can also be NULL in which case the variable is ignored. + * + * *Function Result* A integer return value. + * See result codes listed below. + * Result Codes: + * 0 Success. A set of process PIDs were found and are located in + * ArrayOfReturnedPIDs array. + * -1 Could not find a process with a matching process name + * (i.e. process not found). + * -2 Invalid arguments passed. + * -3 Unable to get the size of sysctl buffer required + * (consult SysctlError return value for more information) + * -4 Unable to allocate memory to store BSD process information + * (consult SysctlError return value for more information) + * -5 The array passed to hold the returned PIDs is not large enough + * to hold all PIDs of process with matching names. + * + *****************************************************/ +int GetAllPIDsForProcessName(const char* ProcessName, + pid_t ArrayOfReturnedPIDs[], + const unsigned int NumberOfPossiblePIDsInArray, + unsigned int* NumberOfMatchesFound, + int* SysctlError); //Can be NULL + +/***************************************************** + * GetPIDForProcessName + ***************************************************** + * Purpose: A convience call for GetAllPIDsForProcessName(). + * This function looks up a process PID given a BSD process + * name. + * + * Parameters: + * ProcessName A constant C-string. On calling + * GetPIDForProcessName this variable holds the BSD process name + * used to do the process lookup. Note that the process name you need + * to pass is the name of the BSD executable process. If trying + * to find the PID of an regular OSX application you will need to pass the + * name of the actual BSD executable inside an application bundle (rather + * than the bundle name itself). In any case as a user you can find the + * BSD process name of any process (including OSX applications) by + * typing the command "ps -axcocommand,pid" in terminal. + * + * *Function Result* A integer return value. + * See result codes listed below. + * Result Codes: + * >=0 Success. The value returned is the PID of the + * requested process. + * -1 Error getting PID for requested process. This error can + * be caused by several things. One is if no such process exists. + * Another is if more than one process has the given name. The + * Answer is to call GetAllPIDsForProcessName() + * for complete error code or to get PIDs if there are multiple + * processes with that name. + *****************************************************/ +int GetPIDForProcessName(const char* ProcessName); + +#if defined(__cplusplus) +} +#endif + +#endif \ No newline at end of file diff --git a/source/S3Decompression.cpp b/source/S3Decompression.cpp new file mode 100755 index 0000000..a56744e --- /dev/null +++ b/source/S3Decompression.cpp @@ -0,0 +1,981 @@ +#include "S3Decompression.h" +typedef UInt32 DWORD; +typedef UInt16 WORD; +typedef UInt8 BYTE; + +#if UNITY_OSX +#define BigEndian 1 +#else +#define BigEndian 0 +#endif + +struct DXTColBlock +{ + WORD col0; + WORD col1; + + // no bit fields - use bytes + BYTE row[4]; +}; + +struct DXTAlphaBlockExplicit +{ + WORD row[4]; +}; + +struct DXTAlphaBlock3BitLinear +{ + BYTE alpha0; + BYTE alpha1; + + BYTE stuff[6]; +}; + +#if BigEndian + +// use cast to struct instead of RGBA_MAKE as struct is +// much +struct Color8888 +{ + BYTE a; + BYTE b; // Last one is MSB, 1st is LSB. + BYTE g; // order of the output ARGB or BGRA, etc... + BYTE r; // change the order of names to change the +}; + +static inline void ByteSwap(UInt16& i) +{ + i = static_cast((i << 8) | (i >> 8)); +} + +struct Color565 +{ + unsigned nBlue : 5; // order of names changes + unsigned nGreen : 6; // byte order of output to 32 bit + unsigned nRed : 5; +}; + +#else + +static inline void ByteSwap(UInt16& i) { } + +// use cast to struct instead of RGBA_MAKE as struct is +// much +struct Color8888 +{ + BYTE r; // change the order of names to change the + BYTE g; // order of the output ARGB or BGRA, etc... + BYTE b; // Last one is MSB, 1st is LSB. + BYTE a; +}; + +struct Color565 +{ + unsigned nBlue : 5; // order of names changes + unsigned nGreen : 6; // byte order of output to 32 bit + unsigned nRed : 5; +}; + + +#endif + +inline void GetColorBlockColors( DXTColBlock * pBlock, Color8888 * col_0, Color8888 * col_1, + Color8888 * col_2, Color8888 * col_3, + WORD & wrd ) +{ + // There are 4 methods to use - see the Time_ functions. + // 1st = shift = does normal approach per byte for color comps + // 2nd = use freak variable bit field color565 for component extraction + // 3rd = use super-freak DWORD adds BEFORE shifting the color components + // This lets you do only 1 add per color instead of 3 BYTE adds and + // might be faster + // Call RunTimingSession() to run each of them & output result to txt file + + + // freak variable bit structure method + // normal math + // This method is fastest + WORD col0 = pBlock->col0; + ByteSwap (col0); + WORD col1 = pBlock->col1; + ByteSwap (col1); + + Color565 * pCol; + + pCol = (Color565*) & (col0 ); + + col_0->a = 0xff; + col_0->r = pCol->nRed; + col_0->r <<= 3; // shift to full precision + col_0->g = pCol->nGreen; + col_0->g <<= 2; + col_0->b = pCol->nBlue; + col_0->b <<= 3; + + pCol = (Color565*) & (col1 ); + col_1->a = 0xff; + col_1->r = pCol->nRed; + col_1->r <<= 3; // shift to full precision + col_1->g = pCol->nGreen; + col_1->g <<= 2; + col_1->b = pCol->nBlue; + col_1->b <<= 3; + + + if( col0 > col1 ) + { + // Four-color block: derive the other two colors. + // 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3 + // These two bit codes correspond to the 2-bit fields + // stored in the 64-bit block. + + wrd = ((WORD)col_0->r * 2 + (WORD)col_1->r )/3; + // no +1 for rounding + // as bits have been shifted to 888 + col_2->r = (BYTE)wrd; + + wrd = ((WORD)col_0->g * 2 + (WORD)col_1->g )/3; + col_2->g = (BYTE)wrd; + + wrd = ((WORD)col_0->b * 2 + (WORD)col_1->b )/3; + col_2->b = (BYTE)wrd; + col_2->a = 0xff; + + wrd = ((WORD)col_0->r + (WORD)col_1->r *2 )/3; + col_3->r = (BYTE)wrd; + + wrd = ((WORD)col_0->g + (WORD)col_1->g *2 )/3; + col_3->g = (BYTE)wrd; + + wrd = ((WORD)col_0->b + (WORD)col_1->b *2 )/3; + col_3->b = (BYTE)wrd; + col_3->a = 0xff; + + } + else + { + // Three-color block: derive the other color. + // 00 = color_0, 01 = color_1, 10 = color_2, + // 11 = transparent. + // These two bit codes correspond to the 2-bit fields + // stored in the 64-bit block. + + // explicit for each component, unlike some refrasts... + + // TRACE("block has alpha\n"); + + wrd = ((WORD)col_0->r + (WORD)col_1->r )/2; + col_2->r = (BYTE)wrd; + wrd = ((WORD)col_0->g + (WORD)col_1->g )/2; + col_2->g = (BYTE)wrd; + wrd = ((WORD)col_0->b + (WORD)col_1->b )/2; + col_2->b = (BYTE)wrd; + col_2->a = 0xff; + + col_3->r = 0x00; // random color to indicate alpha + col_3->g = 0xff; + col_3->b = 0xff; + col_3->a = 0x00; + + } +} // Get color block colors (...) + + + +inline void DecodeColorBlock( DWORD * pImPos, DXTColBlock * pColorBlock, int width, + DWORD * col_0, + DWORD * col_1, DWORD * col_2, DWORD * col_3 ) +{ + // width is width of image in pixels + + + DWORD bits; + int r,n; + + // bit masks = 00000011, 00001100, 00110000, 11000000 + const DWORD masks[] = { 3, 12, 3 << 4, 3 << 6 }; + const int shift[] = { 0, 2, 4, 6 }; + + // r steps through lines in y + for( r=0; r < 4; r++, pImPos += width-4 ) // no width*4 as DWORD ptr inc will *4 + { + + // width * 4 bytes per pixel per line + // each j dxtc row is 4 lines of pixels + + // pImPos = (DWORD*)((DWORD)pBase + i*16 + (r+j*4) * m_nWidth * 4 ); + + // n steps through pixels + for( n=0; n < 4; n++ ) + { + bits = pColorBlock->row[r] & masks[n]; + bits >>= shift[n]; +// AssertIf (bits < 0 || bits > 3); + switch( bits ) + { + case 0 : + *pImPos = *col_0; + pImPos++; // increment to next DWORD + break; + case 1 : + *pImPos = *col_1; + pImPos++; + break; + case 2 : + *pImPos = *col_2; + pImPos++; + break; + case 3 : + *pImPos = *col_3; + pImPos++; + break; + } + } + } +} + + + +inline void DecodeAlphaExplicit( DWORD * pImPos, DXTAlphaBlockExplicit * pAlphaBlock, + int width, DWORD alphazero ) +{ + // alphazero is a bit mask that when & with the image color + // will zero the alpha bits, so if the image DWORDs are + // ARGB then alphazero will be 0x00ffffff or if + // RGBA then alphazero will be 0xffffff00 + // alphazero constructed automaticaly from field order of Color8888 structure + + // decodes to 32 bit format only + + int row, pix; + + WORD wrd; + + Color8888 col; + col.r = col.g = col.b = 0; + + + //TRACE("\n"); + + for( row=0; row < 4; row++, pImPos += width-4 ) + { + // pImPow += pImPos += width-4 moves to next row down + + wrd = pAlphaBlock->row[ row ]; + ByteSwap (wrd); + + // TRACE("0x%.8x\t\t", wrd); + + for( pix = 0; pix < 4; pix++ ) + { + // zero the alpha bits of image pixel + *pImPos &= alphazero; + + col.a = wrd & 0x000f; // get only low 4 bits +// col.a <<= 4; // shift to full byte precision + // NOTE: with just a << 4 you'll never have alpha + // of 0xff, 0xf0 is max so pure shift doesn't quite + // cover full alpha range. + // It's much cheaper than divide & scale though. + // To correct for this, and get 0xff for max alpha, + // or the low bits back in after left shifting + col.a = col.a | (col.a << 4 ); // This allows max 4 bit alpha to be 0xff alpha + // in final image, and is crude approach to full + // range scale + + *pImPos |= *((DWORD*)&col); // or the bits into the prev. nulled alpha + + wrd >>= 4; // move next bits to lowest 4 + + pImPos++; // move to next pixel in the row + + } + } +} + + + + +BYTE gBits[4][4]; +WORD gAlphas[8]; +Color8888 gACol[4][4]; + + +inline void DecodeAlpha3BitLinear( DWORD * pImPos, DXTAlphaBlock3BitLinear * pAlphaBlock, + int width, DWORD alphazero) +{ + + gAlphas[0] = pAlphaBlock->alpha0; + gAlphas[1] = pAlphaBlock->alpha1; + + + // 8-alpha or 6-alpha block? + + if( gAlphas[0] > gAlphas[1] ) + { + // 8-alpha block: derive the other 6 alphas. + // 000 = alpha_0, 001 = alpha_1, others are interpolated + + gAlphas[2] = ( 6 * gAlphas[0] + gAlphas[1]) / 7; // bit code 010 + gAlphas[3] = ( 5 * gAlphas[0] + 2 * gAlphas[1]) / 7; // Bit code 011 + gAlphas[4] = ( 4 * gAlphas[0] + 3 * gAlphas[1]) / 7; // Bit code 100 + gAlphas[5] = ( 3 * gAlphas[0] + 4 * gAlphas[1]) / 7; // Bit code 101 + gAlphas[6] = ( 2 * gAlphas[0] + 5 * gAlphas[1]) / 7; // Bit code 110 + gAlphas[7] = ( gAlphas[0] + 6 * gAlphas[1]) / 7; // Bit code 111 + } + else + { + // 6-alpha block: derive the other alphas. + // 000 = alpha_0, 001 = alpha_1, others are interpolated + + gAlphas[2] = (4 * gAlphas[0] + gAlphas[1]) / 5; // Bit code 010 + gAlphas[3] = (3 * gAlphas[0] + 2 * gAlphas[1]) / 5; // Bit code 011 + gAlphas[4] = (2 * gAlphas[0] + 3 * gAlphas[1]) / 5; // Bit code 100 + gAlphas[5] = ( gAlphas[0] + 4 * gAlphas[1]) / 5; // Bit code 101 + gAlphas[6] = 0; // Bit code 110 + gAlphas[7] = 255; // Bit code 111 + } + + + // Decode 3-bit fields into array of 16 BYTES with same value + + // first two rows of 4 pixels each: + // pRows = (Alpha3BitRows*) & ( pAlphaBlock->stuff[0] ); + const DWORD mask = 0x00000007; // bits = 00 00 01 11 + + DWORD bits = *( (DWORD*) & ( pAlphaBlock->stuff[0] )); + + gBits[0][0] = (BYTE)( bits & mask ); + bits >>= 3; + gBits[0][1] = (BYTE)( bits & mask ); + bits >>= 3; + gBits[0][2] = (BYTE)( bits & mask ); + bits >>= 3; + gBits[0][3] = (BYTE)( bits & mask ); + bits >>= 3; + gBits[1][0] = (BYTE)( bits & mask ); + bits >>= 3; + gBits[1][1] = (BYTE)( bits & mask ); + bits >>= 3; + gBits[1][2] = (BYTE)( bits & mask ); + bits >>= 3; + gBits[1][3] = (BYTE)( bits & mask ); + + // now for last two rows: + + bits = *( (DWORD*) & ( pAlphaBlock->stuff[3] )); // last 3 bytes + + gBits[2][0] = (BYTE)( bits & mask ); + bits >>= 3; + gBits[2][1] = (BYTE)( bits & mask ); + bits >>= 3; + gBits[2][2] = (BYTE)( bits & mask ); + bits >>= 3; + gBits[2][3] = (BYTE)( bits & mask ); + bits >>= 3; + gBits[3][0] = (BYTE)( bits & mask ); + bits >>= 3; + gBits[3][1] = (BYTE)( bits & mask ); + bits >>= 3; + gBits[3][2] = (BYTE)( bits & mask ); + bits >>= 3; + gBits[3][3] = (BYTE)( bits & mask ); + + + // decode the codes into alpha values + int row, pix; + + + for( row = 0; row < 4; row++ ) + { + for( pix=0; pix < 4; pix++ ) + { + gACol[row][pix].a = (BYTE) gAlphas[ gBits[row][pix] ]; + +// AssertIf( gACol[row][pix].r != 0 ); +// AssertIf( gACol[row][pix].g != 0 ); +// AssertIf( gACol[row][pix].b == 0 ); + } + } + + + + // Write out alpha values to the image bits + + for( row=0; row < 4; row++, pImPos += width-4 ) + { + // pImPow += pImPos += width-4 moves to next row down + + for( pix = 0; pix < 4; pix++ ) + { + // zero the alpha bits of image pixel + *pImPos &= alphazero; + + *pImPos |= *((DWORD*) &(gACol[row][pix])); // or the bits into the prev. nulled alpha + pImPos++; + } + } +} + + + +void DecompressDXT1(int width, int height, DWORD* m_pCompBytes, DWORD* m_pDecompBytes) +{ + // This was hacked up pretty quick & slopily + // decompresses to 32 bit format 0xARGB + + int xblocks, yblocks; + + xblocks = width / 4; + yblocks = height / 4; + + + int i,j; + + DWORD * pBase = (DWORD*) m_pDecompBytes; + DWORD * pImPos = (DWORD*) pBase; // pos in decompressed data + WORD * pPos = (WORD*) m_pCompBytes; // pos in compressed data + + DXTColBlock * pBlock; + + Color8888 col_0, col_1, col_2, col_3; + + + WORD wrd; + + for( j=0; j < yblocks; j++ ) + { + // 8 bytes per block + pBlock = (DXTColBlock*) ( (DWORD)m_pCompBytes + j * xblocks * 8 ); + + + for( i=0; i < xblocks; i++, pBlock++ ) + { + + // inline func: + GetColorBlockColors( pBlock, &col_0, &col_1, &col_2, &col_3, wrd ); + + + // now decode the color block into the bitmap bits + // inline func: + + pImPos = (DWORD*)((DWORD)pBase + i*16 + (j*4) * width * 4 ); + + + DecodeColorBlock( pImPos, pBlock, width, (DWORD*)&col_0, (DWORD*)&col_1, + (DWORD*)&col_2, (DWORD*)&col_3 ); + + + // Set to RGB test pattern + // pImPos = (DWORD*)((DWORD)pBase + i*4 + j*m_nWidth*4); + // *pImPos = ((i*4) << 16) | ((j*4) << 8 ) | ( (63-i)*4 ); + + // checkerboard of only col_0 and col_1 basis colors: + // pImPos = (DWORD*)((DWORD)pBase + i*8 + j*m_nWidth*8); + // *pImPos = *((DWORD*)&col_0); + // pImPos += 1 + m_nWidth; + // *pImPos = *((DWORD*)&col_1); + + } + } +} + + +void DecompressDXT3(int width, int height, DWORD* m_pCompBytes, DWORD* m_pDecompBytes) +{ + int xblocks, yblocks; + + xblocks = width / 4; + yblocks = height / 4; + + + int i,j; + + DWORD * pBase = (DWORD*) m_pDecompBytes; + DWORD * pImPos = (DWORD*) pBase; // pos in decompressed data + WORD * pPos = (WORD*) m_pCompBytes; // pos in compressed data + + DXTColBlock * pBlock; + DXTAlphaBlockExplicit * pAlphaBlock; + + Color8888 col_0, col_1, col_2, col_3; + + + WORD wrd; + + // fill alphazero with appropriate value to zero out alpha when + // alphazero is ANDed with the image color 32 bit DWORD: + col_0.a = 0; + col_0.r = col_0.g = col_0.b = 0xff; + DWORD alphazero = *((DWORD*) &col_0); + + + for( j=0; j < yblocks; j++ ) + { + // 8 bytes per block + // 1 block for alpha, 1 block for color + + pBlock = (DXTColBlock*) ( (DWORD)m_pCompBytes + j * xblocks * 16 ); + + for( i=0; i < xblocks; i++, pBlock ++ ) + { + + // inline + // Get alpha block + + pAlphaBlock = (DXTAlphaBlockExplicit*) pBlock; + + // inline func: + // Get color block & colors + pBlock++; + GetColorBlockColors( pBlock, &col_0, &col_1, &col_2, &col_3, wrd ); + + // Decode the color block into the bitmap bits + // inline func: + + pImPos = (DWORD*)((DWORD)pBase + i*16 + (j*4) * width * 4 ); + + + DecodeColorBlock( pImPos, pBlock, width, (DWORD*)&col_0, (DWORD*)&col_1, + (DWORD*)&col_2, (DWORD*)&col_3 ); + + // Overwrite the previous alpha bits with the alpha block + // info + // inline func: + DecodeAlphaExplicit( pImPos, pAlphaBlock, width, alphazero ); + + + } + } +} + + + +void DecompressDXT5(int width, int height, DWORD* m_pCompBytes, DWORD* m_pDecompBytes) +{ + + int xblocks, yblocks; + + xblocks = width / 4; + yblocks = height / 4; + + + int i,j; + + DWORD * pBase = (DWORD*) m_pDecompBytes; + DWORD * pImPos = (DWORD*) pBase; // pos in decompressed data + WORD * pPos = (WORD*) m_pCompBytes; // pos in compressed data + + DXTColBlock * pBlock; + DXTAlphaBlock3BitLinear * pAlphaBlock; + + Color8888 col_0, col_1, col_2, col_3; + WORD wrd; + + // fill alphazero with appropriate value to zero out alpha when + // alphazero is ANDed with the image color 32 bit DWORD: + col_0.a = 0; + col_0.r = col_0.g = col_0.b = 0xff; + DWORD alphazero = *((DWORD*) &col_0); + + //////////////////////////////// + // TRACE("blocks: x: %d y: %d\n", xblocks, yblocks ); + + for( j=0; j < yblocks; j++ ) + { + // 8 bytes per block + // 1 block for alpha, 1 block for color + + pBlock = (DXTColBlock*) ( (DWORD)m_pCompBytes + j * xblocks * 16 ); + + for( i=0; i < xblocks; i++, pBlock ++ ) + { + + // inline + // Get alpha block + + pAlphaBlock = (DXTAlphaBlock3BitLinear*) pBlock; + + // inline func: + // Get color block & colors + pBlock++; + + GetColorBlockColors( pBlock, &col_0, &col_1, &col_2, &col_3, wrd ); + + // Decode the color block into the bitmap bits + // inline func: + + pImPos = (DWORD*)((DWORD)pBase + i*16 + (j*4) * width * 4 ); + + + DecodeColorBlock( pImPos, pBlock, width, (DWORD*)&col_0, (DWORD*)&col_1, + (DWORD*)&col_2, (DWORD*)&col_3 ); + + // Overwrite the previous alpha bits with the alpha block + // info + + DecodeAlpha3BitLinear( pImPos, pAlphaBlock, width, alphazero ); + + + } + } +} // dxt5 + + + + + +/* + +inline void GetColorBlockColors_m2( DXTColBlock * pBlock, Color8888 * col_0, Color8888 * col_1, + Color8888 * col_2, Color8888 * col_3, + WORD & wrd ) +{ + // method 2 + // freak variable bit structure method + // normal math + + Color565 * pCol; + + pCol = (Color565*) & (pBlock->col0 ); + + col_0->a = 0xff; + col_0->r = pCol->nRed; + col_0->r <<= 3; // shift to full precision + col_0->g = pCol->nGreen; + col_0->g <<= 2; + col_0->b = pCol->nBlue; + col_0->b <<= 3; + + pCol = (Color565*) & (pBlock->col1 ); + col_1->a = 0xff; + col_1->r = pCol->nRed; + col_1->r <<= 3; // shift to full precision + col_1->g = pCol->nGreen; + col_1->g <<= 2; + col_1->b = pCol->nBlue; + col_1->b <<= 3; + + + if( pBlock->col0 > pBlock->col1 ) + { + // Four-color block: derive the other two colors. + // 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3 + // These two bit codes correspond to the 2-bit fields + // stored in the 64-bit block. + + wrd = ((WORD)col_0->r * 2 + (WORD)col_1->r )/3; + // no +1 for rounding + // as bits have been shifted to 888 + col_2->r = (BYTE)wrd; + + wrd = ((WORD)col_0->g * 2 + (WORD)col_1->g )/3; + col_2->g = (BYTE)wrd; + + wrd = ((WORD)col_0->b * 2 + (WORD)col_1->b )/3; + col_2->b = (BYTE)wrd; + col_2->a = 0xff; + + wrd = ((WORD)col_0->r + (WORD)col_1->r *2 )/3; + col_3->r = (BYTE)wrd; + + wrd = ((WORD)col_0->g + (WORD)col_1->g *2 )/3; + col_3->g = (BYTE)wrd; + + wrd = ((WORD)col_0->b + (WORD)col_1->b *2 )/3; + col_3->b = (BYTE)wrd; + col_3->a = 0xff; + + } + else + { + // Three-color block: derive the other color. + // 00 = color_0, 01 = color_1, 10 = color_2, + // 11 = transparent. + // These two bit codes correspond to the 2-bit fields + // stored in the 64-bit block. + + // explicit for each component, unlike some refrasts... + + // TRACE("block has alpha\n"); + + wrd = ((WORD)col_0->r + (WORD)col_1->r )/2; + col_2->r = (BYTE)wrd; + wrd = ((WORD)col_0->g + (WORD)col_1->g )/2; + col_2->g = (BYTE)wrd; + wrd = ((WORD)col_0->b + (WORD)col_1->b )/2; + col_2->b = (BYTE)wrd; + col_2->a = 0xff; + + col_3->r = 0x00; // random color to indicate alpha + col_3->g = 0xff; + col_3->b = 0xff; + col_3->a = 0x00; + + } +} + +*/ +/* +inline void GetColorBlockColors_m3( DXTColBlock * pBlock, Color8888 * col_0, Color8888 * col_1, + Color8888 * col_2, Color8888 * col_3, + WORD & wrd ) +{ + // method 3 + ////////////////////////////////////////////////////// + // super-freak variable bit structure with + // Cool Math Trick (tm) + + // Do 2/3 1/3 math BEFORE bit shift on the whole DWORD + // as the fields will NEVER carry into the next + // or overflow!! =) + + Color565 * pCol; + + pCol = (Color565*) & (pBlock->col0 ); + + col_0->a = 0x00; // must set to 0 to avoid overflow in DWORD add + col_0->r = pCol->nRed; + col_0->g = pCol->nGreen; + col_0->b = pCol->nBlue; + + pCol = (Color565*) & (pBlock->col1 ); + col_1->a = 0x00; + col_1->r = pCol->nRed; + col_1->g = pCol->nGreen; + col_1->b = pCol->nBlue; + + if( pBlock->col0 > pBlock->col1 ) + { + *((DWORD*)col_2) = ( (*((DWORD*)col_0)) * 2 + (*((DWORD*)col_1)) ); + + *((DWORD*)col_3) = ( (*((DWORD*)col_0)) + (*((DWORD*)col_1)) * 2 ); + + // now shift to appropriate precision & divide by 3. + col_2->r = ((WORD)col_2->r << 3) / (WORD)3; + col_2->g = ((WORD)col_2->g << 2) / (WORD)3; + col_2->b = ((WORD)col_2->b << 3) / (WORD)3; + + col_3->r = ((WORD)col_3->r << 3) / (WORD)3; + col_3->g = ((WORD)col_3->g << 2) / (WORD)3; + col_3->b = ((WORD)col_3->b << 3) / (WORD)3; + + + col_0->a = 0xff; // now set appropriate alpha + col_1->a = 0xff; + col_2->a = 0xff; + col_3->a = 0xff; + } + else + { + *((DWORD*)col_2) = ( (*((DWORD*)col_0)) + (*((DWORD*)col_1)) ); + + // now shift to appropriate precision & divide by 2. + // << 3 ) / 2 == << 2 + // << 2 ) / 2 == << 1 + col_2->r = ((WORD)col_2->r << 2); + col_2->g = ((WORD)col_2->g << 1); + col_2->b = ((WORD)col_2->b << 2); + + col_2->a = 0xff; + + col_3->a = 0x00; // + col_3->r = 0x00; // random color to indicate alpha + col_3->g = 0xff; + col_3->b = 0xff; + } + + // now shift orig color components + col_0->r <<= 3; + col_0->g <<= 2; + col_0->b <<= 3; + + col_1->r <<= 3; + col_1->g <<= 2; + col_1->b <<= 3; +} + +*/ +/* +inline void GetColorBlockColors_m4( DXTColBlock * pBlock, Color8888 * col_0, Color8888 * col_1, + Color8888 * col_2, Color8888 * col_3, + WORD & wrd ) +{ + + // m1 color extraction from 5-6-5 + // m3 color math on DWORD before bit shift to full precision + + + wrd = pBlock->col0; + col_0->a = 0x00; // must set to 0 to avoid possible overflow & carry to next field in DWORD add + + // extract r,g,b bits + col_0->b = (unsigned char) wrd & 0x1f; // 0x1f = 0001 1111 to mask out upper 3 bits + wrd >>= 5; + col_0->g = (unsigned char) wrd & 0x3f; // 0x3f = 0011 1111 to mask out upper 2 bits + wrd >>= 6; + col_0->r = (unsigned char) wrd & 0x1f; + + + // same for col # 2: + wrd = pBlock->col1; + col_1->a = 0x00; // must set to 0 to avoid possible overflow in DWORD add + + // extract r,g,b bits + col_1->b = (unsigned char) wrd & 0x1f; + wrd >>= 5; + col_1->g = (unsigned char) wrd & 0x3f; + wrd >>= 6; + col_1->r = (unsigned char) wrd & 0x1f; + + + if( pBlock->col0 > pBlock->col1 ) + { + *((DWORD*)col_2) = ( (*((DWORD*)col_0)) * 2 + (*((DWORD*)col_1)) ); + + *((DWORD*)col_3) = ( (*((DWORD*)col_0)) + (*((DWORD*)col_1)) * 2 ); + + // shift to appropriate precision & divide by 3. + col_2->r = ((WORD)col_2->r << 3) / (WORD)3; + col_2->g = ((WORD)col_2->g << 2) / (WORD)3; + col_2->b = ((WORD)col_2->b << 3) / (WORD)3; + + col_3->r = ((WORD)col_3->r << 3) / (WORD)3; + col_3->g = ((WORD)col_3->g << 2) / (WORD)3; + col_3->b = ((WORD)col_3->b << 3) / (WORD)3; + + + col_0->a = 0xff; // set appropriate alpha + col_1->a = 0xff; + col_2->a = 0xff; + col_3->a = 0xff; + } + else + { + *((DWORD*)col_2) = ( (*((DWORD*)col_0)) + (*((DWORD*)col_1)) ); + + // shift to appropriate precision & divide by 2. + // << 3 ) / 2 == << 2 + // << 2 ) / 2 == << 1 + col_2->r = ((WORD)col_2->r << 2); + col_2->g = ((WORD)col_2->g << 1); + col_2->b = ((WORD)col_2->b << 2); + + col_2->a = 0xff; + + col_3->a = 0x00; // + col_3->r = 0x00; // random color to indicate alpha + col_3->g = 0xff; + col_3->b = 0xff; + } + + // shift orig color components to full precision + col_0->r <<= 3; + col_0->g <<= 2; + col_0->b <<= 3; + + col_1->r <<= 3; + col_1->g <<= 2; + col_1->b <<= 3; +} +*/ +/* +inline void GetColorBlockColors_m1( DXTColBlock * pBlock, Color8888 * col_0, Color8888 * col_1, + Color8888 * col_2, Color8888 * col_3, + WORD & wrd ) +{ + + // Method 1: + // Shifty method + + wrd = pBlock->col0; + col_0->a = 0xff; + + // extract r,g,b bits + col_0->b = (unsigned char) wrd; + col_0->b <<= 3; // shift to full precision + wrd >>= 5; + col_0->g = (unsigned char) wrd; + col_0->g <<= 2; // shift to full precision + wrd >>= 6; + col_0->r = (unsigned char) wrd; + col_0->r <<= 3; // shift to full precision + + + // same for col # 2: + wrd = pBlock->col1; + col_1->a = 0xff; + + // extract r,g,b bits + col_1->b = (unsigned char) wrd; + col_1->b <<= 3; // shift to full precision + wrd >>= 5; + col_1->g = (unsigned char) wrd; + col_1->g <<= 2; // shift to full precision + wrd >>= 6; + col_1->r = (unsigned char) wrd; + col_1->r <<= 3; // shift to full precision + + + + // use this for all but the super-freak math method + + if( pBlock->col0 > pBlock->col1 ) + { + // Four-color block: derive the other two colors. + // 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3 + // These two bit codes correspond to the 2-bit fields + // stored in the 64-bit block. + + wrd = ((WORD)col_0->r * 2 + (WORD)col_1->r )/3; + // no +1 for rounding + // as bits have been shifted to 888 + col_2->r = (BYTE)wrd; + + wrd = ((WORD)col_0->g * 2 + (WORD)col_1->g )/3; + col_2->g = (BYTE)wrd; + + wrd = ((WORD)col_0->b * 2 + (WORD)col_1->b )/3; + col_2->b = (BYTE)wrd; + col_2->a = 0xff; + + wrd = ((WORD)col_0->r + (WORD)col_1->r *2 )/3; + col_3->r = (BYTE)wrd; + + wrd = ((WORD)col_0->g + (WORD)col_1->g *2 )/3; + col_3->g = (BYTE)wrd; + + wrd = ((WORD)col_0->b + (WORD)col_1->b *2 )/3; + col_3->b = (BYTE)wrd; + col_3->a = 0xff; + + } + else + { + // Three-color block: derive the other color. + // 00 = color_0, 01 = color_1, 10 = color_2, + // 11 = transparent. + // These two bit codes correspond to the 2-bit fields + // stored in the 64-bit block. + + // explicit for each component, unlike some refrasts... + + // TRACE("block has alpha\n"); + + wrd = ((WORD)col_0->r + (WORD)col_1->r )/2; + col_2->r = (BYTE)wrd; + wrd = ((WORD)col_0->g + (WORD)col_1->g )/2; + col_2->g = (BYTE)wrd; + wrd = ((WORD)col_0->b + (WORD)col_1->b )/2; + col_2->b = (BYTE)wrd; + col_2->a = 0xff; + + col_3->r = 0x00; // random color to indicate alpha + col_3->g = 0xff; + col_3->b = 0xff; + col_3->a = 0x00; + + } +} // Get color block colors (...) + +*/ diff --git a/source/S3Decompression.h b/source/S3Decompression.h new file mode 100755 index 0000000..30ae8ca --- /dev/null +++ b/source/S3Decompression.h @@ -0,0 +1,8 @@ +#ifndef S3DECOMPRESSION_H +#define S3DECOMPRESSION_H + +void DecompressDXT1(int width, int height, UInt32* m_pCompBytes, UInt32* m_pDecompBytes); +void DecompressDXT3(int width, int height, UInt32* m_pCompBytes, UInt32* m_pDecompBytes); +void DecompressDXT5(int width, int height, UInt32* m_pCompBytes, UInt32* m_pDecompBytes); + +#endif diff --git a/source/ai.cpp b/source/ai.cpp new file mode 100644 index 0000000..5da6916 --- /dev/null +++ b/source/ai.cpp @@ -0,0 +1,292 @@ +//ai.cpp +//the game's AI system for computer-controlled opponents +#include +#include "entities.h" +#include "controls.h" +#include "carphysics.h" +#include "gameframe.h" +#include "roads.h" + +#define kAIMaxThottleSlip 0.05 +#define kAIMinSpinAngularVelo (6*PI) +#define kAIThrottleTime 0.2 +#define kAIThrottleReleaseTime 0.4 +#define kAIMaxBrakeSlip 0.05 +#define kAIBrakeTime 0.4 +#define kAIBrakeReleaseTime 0.2 +#define kAIWaypointAheadDistance (gGameInfo->arcade==kGameModeTurbo?25:15) +#define kAIMaxSteering 2 + +float AISteering(tCarDefinition *car,tGameEntity *entity,tVector3 wayPoint,float *overSteer) +{ + // Will adjust the cars steering in order to make the car head to wayPoint. + + tVector3 carDir=!Vector(MatrixGetZVector(entity->dir)->x,0,MatrixGetZVector(entity->dir)->z); + tVector3 wayDir=!Vector(entity->pos.x-wayPoint.x,0,entity->pos.z-wayPoint.z); + tVector3 veloDir=(VectorZero(entity->velo)?carDir:!entity->velo); + *overSteer=(carDir%veloDir).y; + float angleSin=(carDir%wayDir).y; + if(fabs(angleSin)>1.0) + angleSin=sign(angleSin); + float angle=-asin(angleSin); + float steering=angle/car->wheels[0].maxAngle; + if(steering>kAIMaxSteering)steering=kAIMaxSteering; + if(steering<-kAIMaxSteering)steering=-kAIMaxSteering; + return steering; +} + +#define kAIMinCarDistance 4 +#define kAICollisionPredictPrecission 20 +#define kAIMaxCollisionDelay 5.0 +#define kAICarLength 2 +#define kAIVeloAdd 2.0 + +float AIGetCollisionDelay(tGameEntity *entity,tGameEntity **obstacle) +{ + tVector2 basePos1=Vector(entity->pos.x,entity->pos.z); + tVector2 velo1=Vector(entity->velo.x,entity->velo.z); + tVector2 dir1=Vector(MatrixGetZVector(entity->dir)->x,MatrixGetZVector(entity->dir)->z); + float fVelo1=~velo1; + if(fVelo1>0.1) + velo1=velo1*((1/fVelo1)*(fVelo1+kAIVeloAdd)); + else + velo1=dir1*kAIVeloAdd; + int minDelay=kAICollisionPredictPrecission; + for(int i=0;inumPlayers;i++) + if(gCarEntities[i]!=entity) + { + tVector2 pos2=Vector(gCarEntities[i]->pos.x,gCarEntities[i]->pos.z); + tVector2 diff=pos2-basePos1; + if(sqr(diff)=0) + //is the other car close in front of us? + { + tVector2 pos1=basePos1; + tVector2 velo2=Vector(gCarEntities[i]->velo.x,gCarEntities[i]->velo.z); + tVector2 dir2=Vector(MatrixGetZVector(gCarEntities[i]->dir)->x,MatrixGetZVector(gCarEntities[i]->dir)->z); + for(int j=0;jaiPowerCycle); + + //help for players in the back of the field. + float aid=(-phys->lead-8)/22; + if(aid<0)aid=0; + if(aid>1)aid=1; + + return base+aid*0.6; +} + +void AIBrakeThrottle(tCarDefinition *car,tCarPhysics *phys,float velo,float optimalVelo) +{ + float driveSlip=0,slip=0,angularVelo=0; + float maxThrottle=AIMaxThrottle(phys); + + for(int i=0;inumWheels;i++) + { + driveSlip+=phys->wheels[i].slip*car->wheels[i].powered; + slip+=phys->wheels[i].slip; + angularVelo+=fabs(phys->wheels[i].angularVelo*car->wheels[i].powered); + } + slip/=car->numWheels; + float maxSlip=velo<20?0.2-velo/20*(0.2-kAIMaxThottleSlip):kAIMaxThottleSlip; + + if((driveSlip*sign(phys->gear)clutch<=1&&phys->gear<=1)) //traction control + &&velothrottle+=kFrameTime*(1/kAIThrottleTime); + else + phys->throttle-=kFrameTime*(1/kAIThrottleReleaseTime); + + if(phys->throttle>maxThrottle) + phys->throttle=maxThrottle; + else if(phys->throttleidleThrottle) + phys->throttle=phys->idleThrottle; + + if((velo>optimalVelo+1||optimalVelo==0)) + { + if((-slip*sign(angularVelo)brake<0.1)) + phys->brake+=kFrameTime*(1/kAIBrakeTime); + else + phys->brake-=kFrameTime*(1/kAIBrakeTime); + phys->arcadeBrake+=kFrameTime*(1/kAIBrakeTime); + } + else{ + phys->arcadeBrake-=kFrameTime*(1/kAIBrakeReleaseTime); + phys->brake-=kFrameTime*(1/kAIBrakeReleaseTime); + } + + if(phys->arcadeBrake>1) + phys->arcadeBrake=1; + else if(phys->arcadeBrake<0) + phys->arcadeBrake=0; + if(phys->brake>1) + phys->brake=1; + else if(phys->brake<0) + phys->brake=0; +} + +#define kAIOvertakeRoadAnalyseDistance 50 //length of approaching road section to be evaluated for overtaking +#define kAIOvertakeMinVeloDiffPossible 10 +#define kAIOvertakeAbortDistance 60 +#define kAIOvertakeFinishDistance 20 +#define kAIOvertakeLaneChangeTime 3.0 + +void AIEvaluateOvertaking(tGameEntity *overtaker,tGameEntity *obstacle,float track) +{ + tCarPhysics *phys=(tCarPhysics*)overtaker->physics; + if(phys->overtaking)//are we already overtaking? + return; + if(overtaker->velo**MatrixGetZVector(overtaker->dir)<=obstacle->velo**MatrixGetZVector(overtaker->dir)) //are we faster? + return; + float minTrack,maxTrack,minSpeed; + RoadGetWayPointData(overtaker->lastRoadIndex,kAIOvertakeRoadAnalyseDistance,&minTrack,&maxTrack,&minSpeed); + if(minSpeed*0.75<~obstacle->velo+kAIOvertakeMinVeloDiffPossible) //is it possible to drive faster than obstacle all the time? + return; +// if(fabs(minTrack-maxTrack)>1) +// return; + phys->overtaking=obstacle; + phys->overtakeSide=fabs(1+minTrack)>fabs(1-maxTrack)?-1:1; +} + +void ControlEntityAIInput(tGameEntity *entity) +//The game's AI - adjusts all the controls of the car passed in entity. +{ + if(entity->physicsType!=kPhysicsTypeCar)//is entity really a car? + return; + tCarPhysics *phys=(tCarPhysics*)entity->physics; + tCarDefinition *car=&(phys->car); + float velo=~entity->velo; + + + //determine course + float optimalVelo; + float track=phys->overTakeProgress+0.4*sin((gFrameCount*kFrameTime)*0.16+phys->aiRouteCycle); + if(track<-1)track=-1; + else if(track>1)track=1; + tVector3 wayPoint=RoadGetNextWaypoint(entity->pos,&entity->lastRoadIndex,&track,&optimalVelo,kAIWaypointAheadDistance); + + if(gGameInfo->arcade==kGameModeArcade||gGameInfo->arcade==kGameModeTurbo) + optimalVelo*=1.3; + + if(phys->lapCount>gGameInfo->numLaps) + optimalVelo=0; + + //see if we are about to crash into another car if we are to remain on our course. + tGameEntity *obstacle; + float del=AIGetCollisionDelay(entity,&obstacle); + + //if we will crash into another car within the next 5 seconds, see if we can overtake + if(del<=5) + AIEvaluateOvertaking(entity,obstacle,track); + + if(del>5&&phys->stuckTime>10*kFPS) + //if we are stuck, recover car. + { + RoadCenterCar(entity); + phys->overtaking=NULL; + phys->stuckTime=0; + } + + //if we are overtaking + //phys->lightFlags&=~(kLightFlagLIndLight|kLightFlagRIndLight); + if(phys->overtaking) + { + if(fabs(phys->overTakeProgress)<1) + { + phys->overTakeProgress+=(1/kAIOvertakeLaneChangeTime)*kFrameTime*sign(phys->overtakeSide); + if(fabs(phys->overTakeProgress)>1) + phys->overTakeProgress=sign(phys->overTakeProgress); + /*if(phys->lapCount<=gGameInfo->numLaps) + phys->lightFlags|=(phys->overtakeSide==(gGameInfo->reverse?-1:1)?kLightFlagLIndLight:kLightFlagRIndLight);*/ + } + float dist=-(phys->overtaking->pos-entity->pos)**MatrixGetZVector(entity->dir); + if(dist>kAIOvertakeFinishDistance)//are we finished overtaking? + phys->overtaking=NULL; + else if(dist<-kAIOvertakeAbortDistance)//or are we too slow to overtake? + phys->overtaking=NULL; + if(fabs(phys->overtakeSide-track)<0.5) + phys->overtaking=NULL; + } + else if(fabs(phys->overTakeProgress)>0) + { + int s=sign(phys->overTakeProgress); + phys->overTakeProgress-=(1/kAIOvertakeLaneChangeTime)*kFrameTime*s; + //if(phys->lapCount<=gGameInfo->numLaps) + //phys->lightFlags|=(s==-1?kLightFlagLIndLight:kLightFlagRIndLight); + if(s!=sign(phys->overTakeProgress)) + phys->overTakeProgress=0; + } + + //steering + float overSteer; + phys->steering=AISteering(car,entity,wayPoint,&overSteer); + + //brake / throttle + AIBrakeThrottle(car,phys,velo,optimalVelo); + + if(gFrameCount*kFrameTime>=kStartGameDelaySeconds) + { + //gear shifting / clutch control + if(phys->gear==0) + { + phys->gear=1; + phys->lastGearSwitch=gFrameCount*kFrameTime; + } + if(phys->gear>=1&&(gFrameCount-1)*kFrameTime>phys->lastGearSwitch+car->gearSwitchTime&&gFrameCount*kFrameTime>=kStartGameDelaySeconds+1.5) + { + if(phys->rpm>car->maxRPM&&phys->gearnumGears-2) + { + phys->gear++; + phys->lastGearSwitch=gFrameCount*kFrameTime; + } + if(phys->rpmshiftDownRPM&&phys->gear>1) + { + phys->gear--; + phys->lastGearSwitch=gFrameCount*kFrameTime; + } + } + + if(gFrameCount*kFrameTimelastGearSwitch+car->gearSwitchTime) + { + float switchTime=(gFrameCount*kFrameTime-phys->lastGearSwitch)/car->gearSwitchTime; + phys->clutch=switchTime; + if(phys->rpm<2000) + phys->clutch=(phys->rpm-car->idleRPM)/(2000-car->idleRPM); + if(switchTime<0.5) + phys->throttle=phys->idleThrottle; + } + else + if(phys->rpm<2000) + phys->clutch=(phys->rpm-car->idleRPM)/(2000-car->idleRPM); + else + phys->clutch=1; + } + + if(overSteer*velo>20) + if(car->wheels[2].powered) + { + phys->clutch=0; + phys->throttle=0; + } +} diff --git a/source/carphysics.cpp b/source/carphysics.cpp new file mode 100755 index 0000000..c8cd3bf --- /dev/null +++ b/source/carphysics.cpp @@ -0,0 +1,1338 @@ +//carphysics.cpp +//simulation of all forces and tourques moving the cars around + +#include + +#include "stdtypes.h" +#include "reg_tool_3.h" +#include "rt3_redline.h" + +#include "vectors.h" +#include "entities.h" +#include "carphysics.h" +#include "gamemem.h" +#include "gameframe.h" +#include "roads.h" +#include "text.h" +#include "particles.h" +#include "tracks.h" +#include "collision.h" +#include "config.h" +#include "environment.h" +#include "gamesound.h" +#include "renderframe.h" +#include "networkphysics.h" + +#define kMaxSmoke 120.0 + +#define kIndicatorTime 0.5 +#define kMaxTwistAngle (PI/12) +#define kSpinRotations 10.0 +#define kTrackPrecision 5 //each nTH frame a skid mark is generated + +void InstallCarAddOns(tCarPhysics *phys) +{ + tCarDefinition *car=&(phys->car); + for(int i=0;inumAddOns;i++) + //is this add-on installed? + if((phys->addOns>>i)&1) + if(!(phys->addOns&car->addOns[i].conflicts)) + { + car->mass+=car->addOns[i].mass; + car->massCenter=car->massCenter+car->addOns[i].massCenter; + car->rearLift+=car->addOns[i].rearLift; + car->frontLift+=car->addOns[i].frontLift; + car->frontAirResistance+=car->addOns[i].frontAirResistance; + + if(car->addOns[i].powerPercent>0) + { + car->power*=car->addOns[i].powerPercent; + car->powerRPM*=car->addOns[i].powerPercent; + } + if(car->addOns[i].frontSwayBar>0) + car->frontSwayBar=car->addOns[i].frontSwayBar; + if(car->addOns[i].exhaustFire>0) + car->exhaustFire=car->addOns[i].exhaustFire; + if(car->addOns[i].rearSwayBar>0) + car->rearSwayBar=car->addOns[i].rearSwayBar; + if(car->addOns[i].damperStrength>0) + car->damperStrength=car->addOns[i].damperStrength; + if(car->addOns[i].engineInertia>0) + car->engineInertia=car->addOns[i].engineInertia; + if(car->addOns[i].topGearRatio>0) + car->gearRatios[car->numGears-1]=car->addOns[i].topGearRatio; + if(car->addOns[i].maxRPM>0) + car->maxRPM=car->addOns[i].maxRPM; + if(car->addOns[i].track>0) + { + car->wheels[0].pos.x-=car->addOns[i].track; + car->wheels[1].pos.x+=car->addOns[i].track; + car->wheels[2].pos.x-=car->addOns[i].track; + car->wheels[3].pos.x+=car->addOns[i].track; + } + if(car->addOns[i].frontWheelWidth>0) + { + car->wheels[0].width=car->addOns[i].frontWheelWidth; + car->wheels[1].width=car->addOns[i].frontWheelWidth; + } + if(car->addOns[i].rearWheelWidth>0) + { + car->wheels[2].width=car->addOns[i].rearWheelWidth; + car->wheels[3].width=car->addOns[i].rearWheelWidth; + } + if(car->addOns[i].frontMaxSuspension>0) + { + car->wheels[0].maxSuspension=car->addOns[i].frontMaxSuspension; + car->wheels[1].maxSuspension=car->addOns[i].frontMaxSuspension; + } + if(car->addOns[i].rearMaxSuspension>0) + { + car->wheels[2].maxSuspension=car->addOns[i].rearMaxSuspension; + car->wheels[3].maxSuspension=car->addOns[i].rearMaxSuspension; + } + if(car->addOns[i].torquePercent>0) + car->torque*=car->addOns[i].torquePercent; + if(car->addOns[i].differentialLockCoefficient>0) + car->differentialLockCoefficient=car->addOns[i].differentialLockCoefficient; + if(car->addOns[i].finalDriveRatio>0) + car->finalDriveRatio=car->addOns[i].finalDriveRatio; + if(car->addOns[i].gearSwitchTime>0) + car->gearSwitchTime=car->addOns[i].gearSwitchTime; + } +} + + +float WheelGroundDistance(tGameEntity *carEntity,tVector3 wheelPos,tVector3 yDir,tWheelDefinition *wheel,tVector3 *groundNormal,float oldHeight,int *surfaceType,float *bump) +//returns the distance between the wheel at wheelPos and the ground. +//gives the normal of the ground under the wheel in groundNormal +//and the surface type of the ground in surfaceType +{ + tVector3 wheelGroundPoint=wheelPos-yDir*(wheel->radius+oldHeight); + return GetGroundOffsetAndBump(wheelGroundPoint,&carEntity->lastRoadIndex,groundNormal,surfaceType,bump)+oldHeight; +} + +void CalcWheelPositions(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels) +//Calculates the positions and directions/angles of all wheels of the car passed in carEntity. +{ + for(int i=0;inumWheels;i++) + { + wheels[i].pos=car->wheels[i].pos*carEntity->dir; + wheels[i].velo=((wheels[i].pos*carEntity->rVelo)-wheels[i].pos)*kFPS+carEntity->velo; + wheels[i].oldAngle=phys->wheels[i].angle; + if(phys->wheels[i].rotation>=2*PI) + phys->wheels[i].rotation-=2*PI; + else if(phys->wheels[i].rotation<0) + phys->wheels[i].rotation+=2*PI; + phys->wheels[i].angle=car->wheels[i].maxAngle*phys->steering;//*(0.5+0.5*sqr(phys->steering)); + } +} + +void CalcWheelGroundOffsets(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels) +//Calculates the spring supression for all wheels of the car passed in carEntity. +{ + for(int i=0;inumWheels;i++) + { + float dist=WheelGroundDistance(carEntity,wheels[i].pos+carEntity->pos,*MatrixGetYVector(carEntity->dir),&car->wheels[i],&wheels[i].groundNormal,phys->wheels[i].suspension,&phys->wheels[i].surfaceType,&(wheels[i].bump)); + if(wheels[i].onGround=distwheels[i].maxSuspension) + { + phys->wheels[i].suspension=dist; + if(phys->wheels[i].suspension<0) + phys->wheels[i].suspension=0; + } + else + phys->wheels[i].suspension=car->wheels[i].maxSuspension; + } +} + + +void CalcSwayBars(tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels) +//Calculates the Forces generated by the cars Anti-Sway-Bars. +{ + float force; + int arcade=(gGameInfo->arcade==kGameModeArcade||gGameInfo->arcade==kGameModeTurbo); + + force=(phys->wheels[1].suspension-phys->wheels[0].suspension)*(car->frontSwayBar+(arcade?car->mass*10:0)); + wheels[0].suspensionForce+=force; + wheels[1].suspensionForce-=force; + force=(phys->wheels[3].suspension-phys->wheels[2].suspension)*(car->rearSwayBar+(arcade?car->mass*10:0)); + wheels[2].suspensionForce+=force; + wheels[3].suspensionForce-=force; +} + + +void CalcWheelSupension(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels) +//Calculates spring and damper forces +{ + //Calculate the distance of each wheel from car's center of gravity + float totalInvMassDistance=0; + float invMassDistance[kMaxWheels]; + for(int i=0;inumWheels;i++) + { + invMassDistance[i]=1/~(car->massCenter-car->wheels[i].pos); + totalInvMassDistance+=invMassDistance[i]; + } + + //factor used to calculate the spring forces + float totalSpringForceFactor=car->mass*2*gEnvironment->gravity; + + tVector3 zDir=*MatrixGetZVector(carEntity->dir); + float airRes=~carEntity->velo*0.5*fabs(zDir*carEntity->velo)*gEnvironment->airDensity; + for(int i=0;inumWheels;i++) + { + //from the distance to CG, determine the fraction of the cars mass this wheel has to carry. + //not entierly correct, but should suffice for most cases + float massFraction=invMassDistance[i]/totalInvMassDistance; + float calcSuspension=(phys->wheels[i].suspension/car->wheels[i].maxSuspension)*2-1; + calcSuspension=calcSuspension*calcSuspension*calcSuspension; + calcSuspension=(calcSuspension*0.5+0.5)*car->wheels[i].maxSuspension; + calcSuspension=phys->wheels[i].suspension; + //calculate spring force, based on totalSpringForceFactor, massFraction, and the ratio of spring compression + //wheels[i].suspensionForce=(car->wheels[i].maxSuspension-phys->wheels[i].suspension)*totalSpringForceFactor*massFraction/car->wheels[i].maxSuspension; + wheels[i].suspensionForce=(car->wheels[i].maxSuspension-calcSuspension)*totalSpringForceFactor*massFraction/car->wheels[i].maxSuspension; + + //if(gGameInfo->arcade&&gFrameCount*kFrameTime<=1)wheels[i].suspensionForce*=gFrameCount*kFrameTime; + + //if the springs are fully compressed, we need to add another force, applied by the ground, + //which keeps the wheel from falling into the ground + if(!phys->wheels[i].suspension) + { + float aerodynamicForce=airRes*(i<2?car->frontLift:car->rearLift); + + float fGroundForce=-wheels[i].velo*wheels[i].groundNormal*kFPS*car->mass/car->numWheels-aerodynamicForce/2; + if(fGroundForce>car->mass/car->numWheels*gEnvironment->gravity-aerodynamicForce/2) + fGroundForce=car->mass/car->numWheels*gEnvironment->gravity-aerodynamicForce/2; + if(fGroundForce>0) + wheels[i].suspensionForce+=fGroundForce; + } + + //the spring and ground force make up the normal force to the wheel, used to calculate tire traction + wheels[i].normalForce=wheels[i].suspensionForce; + + //and now, calculate damper forces + if(wheels[i].onGround){ + //wheels speed WRT the ground + float wheelYVelo=wheels[i].velo*wheels[i].groundNormal; + + //friction caused by the suspension (constant, not dependant on the speed of wheel Y movement) + float suspensionResistance=sign(wheelYVelo)*car->supsensionFriction*massFraction; + + //force caused by dampers (a linear function of wheel Y velocity) + //dampers stronger in first second to avoid arcade mode shake bug. + suspensionResistance+=wheelYVelo*car->damperStrength*massFraction*((gGameInfo->arcade&&gFrameCount*kFrameTime<=1)?5:1); + + //do not apply forces which are higher than necessary to damp all motion + if(fabs(suspensionResistance)>fabs(wheelYVelo*kFPS*car->mass/car->numWheels)) + suspensionResistance=wheelYVelo*kFPS*car->mass/car->numWheels; + + //to prevent unexpected behavior when loosing ground contact (ie. jumping with the car) + if(suspensionResistance>2*car->mass) + suspensionResistance=2*car->mass; + + //apply damper resistance + wheels[i].suspensionForce-=suspensionResistance; + } + } + + CalcSwayBars(car,phys,wheels); + + int wall=true; + for(int i=0;inumWheels;i++) + if(wheels[i].groundNormal.y!=0) + wall=false; + if(wall) + if(wheels[1].pos.y>wheels[0].pos.y) + { + wheels[1].suspensionForce+=100000; + wheels[3].suspensionForce+=100000; + } + else + { + wheels[0].suspensionForce+=100000; + wheels[2].suspensionForce+=100000; + } +} + + +tVector3 PacejkaCalcWheelRoadForces(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels,int i,float engineTorque,float frictionTorque); + + +float CalcTorque(float rpm,tCarDefinition *car) +//Calculate the maximal torque the car can currently produce, based on engine revs. +//this is a function based on the car's power and torque ratings, and does not +//necessaryly correspond to the actual car's torque curve, but it is usually close enough. +//the advantage of this is that we do not need to know a car's actual torque chart to simulate a car. +{ + float torqueResult; + if(rpmtorqueRPM) + torqueResult=car->torque*(-sqr(rpm/car->torqueRPM-1)+1); + else { + float maxPowerTorque=car->power/(car->powerRPM*2*PI/60); + float aproxFactor=(car->torque-maxPowerTorque)/(2*car->torqueRPM*car->powerRPM-sqr(car->powerRPM)-sqr(car->torqueRPM)); + float torque=aproxFactor*sqr(rpm-car->torqueRPM)+car->torque; + torqueResult=torque>0?torque:0; + } + if(rpm>car->maxRPM) + { + torqueResult*=1-((rpm-car->maxRPM)*0.006); + if(torqueResult<0) + torqueResult=0; + } + if(rpm<0) + torqueResult=0; + return torqueResult; +} + +#include "random.h" +void CalcWheelRoadForceNoClutch(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels) +//Calculater the force the wheels apply onto road when the clutch is fully engaged +{ + //Calculate the drivetrain ratio + float ratio=car->gearRatios[phys->gear+1]*car->finalDriveRatio; + if(gGameInfo->arcade==kGameModeTurbo) + ratio*=0.8; + + //Calculate the engine inertia at the final drive + float inertia=car->engineInertia*sqr(ratio); + + //Calculate the frictional braking torque of the engine (in N*m) + float engineFrictionTorque=25+phys->rpm*0.02; + if(car->engineBaseFriction||car->engineRPMFriction) + engineFrictionTorque=car->engineBaseFriction+car->idleRPM*car->engineRPMFriction; + + //Calculate the current engine torque (in N*m) + float engineTorque=(CalcTorque(phys->rpm,car)+engineFrictionTorque)*phys->throttle; + + if(phys->damage>kFatalDamage) + engineTorque=0; + if(phys->damage>kEngineDamage) + engineTorque=RandomProb(0.6)?engineTorque:0; + + //Calculate average radius of powered wheels + float wheelRadius=0; + for(int i=0;inumWheels;i++) + wheelRadius+=car->wheels[i].radius*car->wheels[i].powered; + + //Calculate average angular velocity of powered wheels + float averageAngularVelo=0; + for(int i=0;inumWheels;i++) + averageAngularVelo+=car->wheels[i].powered*phys->wheels[i].angularVelo*(car->wheels[i].radius/wheelRadius); + + float rpm=0; + for(int i=0;inumWheels;i++) + { + //Calculate the inertia at the wheel + wheels[i].inertia=car->wheels[i].inertia+car->wheels[i].powered*inertia; + + //Calculate the amount of braking applied to the wheel + float braked=phys->brake+phys->handbrake*car->wheels[i].handbraked; + if(braked>1)braked=1; + + float glow=braked*fabs(phys->wheels[i].angularVelo)*0.02; + if(glow>1)glow=1; + phys->wheels[i].glow+=(glow-0.2)*0.3*kFrameTime; + if(phys->wheels[i].glow>1)phys->wheels[i].glow=1; + else if(phys->wheels[i].glow<0)phys->wheels[i].glow=0; + + //Calculate the torque applied by the brakes + float brakeFriction=braked*car->wheels[i].braked+fabs(phys->wheels[i].angularVelo)*wheels[i].normalForce*gSurfaceTypes->types[phys->wheels[i].surfaceType].brakeFactor; + + //Calculate the torque applied by differential lock + float lockingTorque=(averageAngularVelo-phys->wheels[i].angularVelo*(car->wheels[i].radius/wheelRadius))*car->wheels[i].powered*car->differentialLockCoefficient; + + //Calculate the total braking torque (brakes, friction) applied on the wheel + float wheelFrictionTorque=engineFrictionTorque*car->wheels[i].powered*fabs(ratio)+car->wheels[i].friction+brakeFriction; + + //Calculate the engine torque applied on the wheels + float wheelEngineTorque=engineTorque*car->wheels[i].powered*ratio+lockingTorque; + + if(wheels[i].onGround) + //Calculate the force the wheel applies onto the road + wheels[i].roadForce=PacejkaCalcWheelRoadForces(carEntity,car,phys,wheels,i,wheelEngineTorque,wheelFrictionTorque); + else{ + //wheel applies no force onto the road + wheels[i].roadForce=Vector(0,0,0); + phys->wheels[i].slipVelo=0; + + //Calculate wheel's new angular velocity + float angularAcceleration=((wheelEngineTorque-wheelFrictionTorque*sign(phys->wheels[i].angularVelo))/wheels[i].inertia); + if(-angularAcceleration*kFrameTime>phys->wheels[i].angularVelo) + phys->wheels[i].angularVelo=0; + else + phys->wheels[i].angularVelo+=angularAcceleration*kFrameTime; + } + //rotate the wheel according to it's angular velocity + phys->wheels[i].rotation+=phys->wheels[i].angularVelo*kFrameTime; + + //Calulate average wheels angular velocities. + rpm+=phys->wheels[i].angularVelo*car->wheels[i].powered; + } + + //Calculate new engine RPM + phys->engineAngularVelo=rpm*ratio; + phys->rpm=phys->engineAngularVelo*60.0/(2*PI); +} + + +//Calculates how much throttle has to be given to maintain the car's idle RPM +float CalcIdleThrottle(tCarDefinition *car) +{ + float engineFrictionTorque=25+car->idleRPM*0.02; + if(car->engineBaseFriction||car->engineRPMFriction) + engineFrictionTorque=car->engineBaseFriction+car->idleRPM*car->engineRPMFriction; + float engineTorque=CalcTorque(car->idleRPM,car)+engineFrictionTorque; + return engineFrictionTorque/engineTorque; +} + +float CalcEngine(tCarDefinition *car,tCarPhysics *phys) +{ + if(phys->idleThrottle==0) + phys->idleThrottle=CalcIdleThrottle(car); + float rawEngineTorque=CalcTorque(phys->rpm,car); + float engineFrictionTorque=25+phys->rpm*0.02; + if(car->engineBaseFriction||car->engineRPMFriction) + engineFrictionTorque=car->engineBaseFriction+phys->rpm*car->engineRPMFriction; + float engineTorque=(rawEngineTorque+fabs(engineFrictionTorque))*phys->throttle; + if(phys->rpm>5*car->maxRPM)//safety against engine physics getting out of hand. + { + phys->rpm=0; + phys->gear=0; + } + if(phys->damage>kFatalDamage) + engineTorque=0; + if(phys->damage>kEngineDamage) + engineTorque=RandomProb(0.6)?engineTorque:0; + + float engineAngularAcceleration=(engineTorque-engineFrictionTorque)/car->engineInertia; + phys->rpm=phys->engineAngularVelo*(60.0/(2*PI)); + if(phys->rpmidleRPM) + { + phys->rpm=car->idleRPM; + phys->clutch=0; + } + phys->engineAngularVelo+=engineAngularAcceleration*kFrameTime; + return engineAngularAcceleration; +} + +float CalcWheelTorques(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels,float ratio) +{ + float oldDrivetrainAngularVelo=phys->drivetrainAngularVelo; + phys->drivetrainAngularVelo=0; + + //Calculate average radius of powered wheels + float wheelRadius=0; + for(int i=0;inumWheels;i++) + wheelRadius+=car->wheels[i].radius*car->wheels[i].powered; + + //Calculate average angular velocity of powered wheels + float averageAngularVelo=0; + for(int i=0;inumWheels;i++) + averageAngularVelo+=car->wheels[i].powered*phys->wheels[i].angularVelo*(car->wheels[i].radius/wheelRadius); + + for(int i=0;inumWheels;i++) + { + wheels[i].inertia=car->wheels[i].inertia; + + //Calculate the amount of braking applied to the wheel + float braked=phys->brake+phys->handbrake*car->wheels[i].handbraked; + if(braked>1)braked=1; + if(gFrameCount*kFrameTimewheels[i].angularVelo)*0.02; + if(glow>1)glow=1; + phys->wheels[i].glow+=(glow-0.2)*0.3*kFrameTime; + if(phys->wheels[i].glow>1)phys->wheels[i].glow=1; + else if(phys->wheels[i].glow<0)phys->wheels[i].glow=0; + + //Calculate the torque applied by the brakes + float brakeFriction=braked*car->wheels[i].braked+fabs(phys->wheels[i].angularVelo)*wheels[i].normalForce*gSurfaceTypes->types[phys->wheels[i].surfaceType].brakeFactor; + + //Calculate the torque applied by differential lock + float lockingTorque=(averageAngularVelo-phys->wheels[i].angularVelo*(car->wheels[i].radius/wheelRadius))*car->wheels[i].powered*car->differentialLockCoefficient; + + //Calculate the total braking torque (brakes, friction) applied on the wheel + float wheelFrictionTorque=car->wheels[i].friction+brakeFriction; + + float wheelEngineTorque=phys->clutchTorque*car->wheels[i].powered*ratio+lockingTorque; + if(wheels[i].onGround) + wheels[i].roadForce=PacejkaCalcWheelRoadForces(carEntity,car,phys,wheels,i,wheelEngineTorque,wheelFrictionTorque); + else{ + float angularAcceleration=((wheelEngineTorque-wheelFrictionTorque*sign(phys->wheels[i].angularVelo))/wheels[i].inertia); + if(-angularAcceleration*kFrameTime>phys->wheels[i].angularVelo) + phys->wheels[i].angularVelo=0; + else + phys->wheels[i].angularVelo+=angularAcceleration*kFrameTime; + wheels[i].roadForce=Vector(0,0,0); + phys->wheels[i].slipVelo=0; + } + phys->wheels[i].rotation+=phys->wheels[i].angularVelo*kFrameTime; + phys->drivetrainAngularVelo+=phys->wheels[i].angularVelo*car->wheels[i].powered; + } + phys->drivetrainAngularVelo*=ratio; + return (phys->drivetrainAngularVelo-oldDrivetrainAngularVelo)/kFrameTime; +} + +void CalcClutchTorqueTransfer(tCarDefinition *car,tCarPhysics *phys,float ratio,float engineAngularAcceleration,float drivetrainAngularAcceleration) +{ + if(ratio==0||phys->clutch==0) + { + phys->clutchTorque=0; + return; + } + float angularAcceleration=(phys->engineAngularVelo+engineAngularAcceleration*kFrameTime-phys->drivetrainAngularVelo+drivetrainAngularAcceleration*kFrameTime)/kFrameTime; + float i1=car->engineInertia; + float i2=0; + for(int i=0;inumWheels;i++) + if(car->wheels[i].powered) + i2+=car->wheels[i].inertia/sqr(ratio); + float inertia=(i1*i2)/(i1+i2); + float optimalTorque=inertia*angularAcceleration; + + float maxTorque=car->maxClutchTorqueTransfer*phys->clutch*2; + phys->clutchTorque=optimalTorqueengineAngularVelo-=phys->clutchTorque/i1*kFrameTime; +} + +void CalcWheelRoadForceClutch(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels) +{ + float ratio=car->gearRatios[phys->gear+1]*car->finalDriveRatio; + if(gGameInfo->arcade==kGameModeTurbo) + ratio*=0.8; + float engineAngularAcceleration=CalcEngine(car,phys); + float drivetrainAngularAcceleration=CalcWheelTorques(carEntity,car,phys,wheels,ratio); + CalcClutchTorqueTransfer(car,phys,ratio,engineAngularAcceleration,drivetrainAngularAcceleration); +} + +void CalcWheelRoadForce(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels) +{ + if(phys->clutch==1.0&&phys->gear!=0) + CalcWheelRoadForceNoClutch(carEntity,car,phys,wheels); + else + CalcWheelRoadForceClutch(carEntity,car,phys,wheels); +} + +void CalcTotalWheelForce(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels,tVector3 *totalWheelForce,tVector3 *totalWheelTorque) +{ + *totalWheelForce=Vector(0,0,0); + *totalWheelTorque=Vector(0,0,0); + for(int i=0;inumWheels;i++){ + tVector3 wheelForce=wheels[i].roadForce+wheels[i].suspensionForce*wheels[i].groundNormal; + tVector3 wheelGroundPoint=wheels[i].pos-*MatrixGetYVector(carEntity->dir)*(car->wheels[i].radius+phys->wheels[i].suspension); + tVector3 wheelCarTorque=(wheelGroundPoint-car->massCenter*carEntity->dir)%wheelForce; + *totalWheelTorque=*totalWheelTorque+wheelCarTorque; + *totalWheelForce=*totalWheelForce+wheelForce; + } + if(phys->rpmjerkRPM) + phys->rpm=car->jerkRPM; +} + +#define kMaxDraftDistance 60.0 +#define kMaxDraftAngle (PI/15.0) +#define kMaxDraftVeloAngle (PI/10.0) + +float CalcDraftFactor(tGameEntity *car1) +{ + float draftFactor=0; + for(int i=0;inumPlayers;i++) + if(gCarEntities[i]!=car1) + { + tGameEntity *car2=gCarEntities[i]; + tVector3 v=car2->pos-car1->pos; + float dir,veloDir,dist; + + dir=1-acos((!car1->velo)*!v)/kMaxDraftAngle; + if(isnan(dir))dir=1; + if(dir<0)dir=0; + + veloDir=1-acos((!car1->velo)*(!car2->velo))/kMaxDraftVeloAngle; + if(isnan(veloDir))veloDir=1; + if(veloDir<0)veloDir=0; + + dist=1-(~v)/kMaxDraftDistance; + if(dist<0)dist=0; + + draftFactor+=dir*veloDir*dist; + } + if(draftFactor>1) + draftFactor=1; + return draftFactor; +} + +//#define __NO_AERODYNAMICS +void CalcAerodynamics(tGameEntity *carEntity,tCarDefinition *car,tVector3 *aerodynamicForce,tVector3 *aerodynamicTorque) +{ +#ifndef __NO_AERODYNAMICS + tVector3 xDir=*MatrixGetXVector(carEntity->dir); + tVector3 yDir=*MatrixGetYVector(carEntity->dir); + tVector3 zDir=*MatrixGetZVector(carEntity->dir); + float fVelo=~carEntity->velo; + float draftFactor=CalcDraftFactor(carEntity); + + float airRes=fVelo*0.5*fabs(zDir*carEntity->velo)*gEnvironment->airDensity; + tVector3 frontSpoilPoint=Vector(0,car->massCenter.y,car->wheels[0].pos.z)*carEntity->dir; + tVector3 rearSpoilPoint=Vector(0,car->massCenter.y,car->wheels[2].pos.z)*carEntity->dir; + *aerodynamicForce=yDir*airRes*(car->frontLift+car->rearLift); + *aerodynamicTorque=(frontSpoilPoint-car->massCenter*carEntity->dir)%(yDir*airRes*(car->frontLift))+(rearSpoilPoint-car->massCenter*carEntity->dir)%(yDir*airRes*(car->rearLift)); + + tVector3 dragCenter=Vector(0,car->massCenter.y,0); + float dragFactor=fabs(zDir*carEntity->velo*car->frontAirResistance)+fabs(yDir*carEntity->velo*car->topAirResistance)+fabs(xDir*carEntity->velo*car->sideAirResistance); + *aerodynamicForce=*aerodynamicForce-carEntity->velo*0.5*dragFactor*gEnvironment->airDensity*(1-0.9*draftFactor); + *aerodynamicTorque=*aerodynamicTorque+((dragCenter-car->massCenter)*carEntity->dir)%*aerodynamicForce; +#else + *aerodynamicForce=Vector(0,0,0); + *aerodynamicTorque=Vector(0,0,0); +#endif +} + +void CarLights(tCarPhysics *phys) +{ + phys->lightFlags&=~(kLightFlagRevLight+kLightFlagBrakeLight); + if((int)(gFrameCount*kFrameTime/kIndicatorTime)&1) + phys->lightFlags&=~(kLightFlagLIndLight+kLightFlagRIndLight); + if(phys->brake>0) + phys->lightFlags|=kLightFlagBrakeLight; + if(phys->gear==-1) + phys->lightFlags|=kLightFlagRevLight; + if(gEnvironment->spotLightEnable&&gFrameCount==0) + phys->lightFlags|=kLightFlagDriveLight; +} + +//Create Tracks and Particle Effects for the car +void CarCreateEffects(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels) +{ + float totalSlip=0; + + for(int i=0;inumWheels;i++) + { + tSurfaceType *surf=gSurfaceTypes->types+phys->wheels[i].surfaceType; + + totalSlip+=fabs(phys->wheels[i].slipVelo)/car->numWheels; + + //does this surface smoke? + if(wheels[i].onGround) + if(surf->smokeEnable&&!((gCameraMode==kCameraCockpit||gCameraMode==kCameraCockpitCarHidden)&&carEntity==gViewedEntity)) + { + //is the wheel slipping enough to smoke? + float smoke=(fabs(phys->wheels[i].slipVelo)-surf->minSmokeSlideVelo)/(surf->maxSmokeSlideVelo-surf->minSmokeSlideVelo); + if(smoke>0) + { + //create particles + tParticlesDef def; + ParticlesInitDef(&def); + + if(smoke>1)smoke=1; + smoke*=kMaxSmoke; + def.sprite=surf->smokeTexture; + def.maxSpread=surf->smokeSpread; + def.maxVelo=surf->smokeMaxVelo; + def.gravity=surf->smokeGravity; + def.maxLife=surf->smokeMaxLife; + def.brightness=0.5+(gEnvironment->ambient.x+gEnvironment->ambient.y+gEnvironment->ambient.z)*0.166; + def.screenTexture=surf->smokeStickEnable?surf->smokeTexture:-1; + def.xSize=surf->smokeSize;def.ySize=surf->smokeSize; + tVector3 baseVelo=Vector(0,0.03,-0.1*phys->wheels[i].slip)*carEntity->dir*surf->smokeSpeed+carEntity->velo*0.3; + ParticlesCreate(smoke*kFrameTime,wheels[i].pos+carEntity->pos,baseVelo,&def); + } + } + + //does this surface leave tracks? + if(surf->trackEnable&&!((gCameraMode==kCameraCockpit||gCameraMode==kCameraCockpitCarHidden)&&carEntity==gViewedEntity)) + //we only generate tracks every kTrackPrecision-th frame for performance reasons + if(!(gFrameCount%kTrackPrecision)) + { + //Calculate track intensity + float tracks=(fabs(phys->wheels[i].slipVelo)-surf->minTrackSlideVelo)/(surf->maxTrackSlideVelo-surf->minTrackSlideVelo); + if(tracks>0&&wheels[i].onGround) + { + //create tracks. + if(tracks>1)tracks=1; + tVector3 pos1=wheels[i].pos+carEntity->pos-wheels[i].groundNormal*(phys->wheels[i].suspension+car->wheels[i].radius); + pos1.y-=wheels[i].bump; + tVector3 pos2=pos1+wheels[i].velo*kFrameTime; + phys->wheels[i].lastTrack=TracksAdd(pos1,pos2,wheels[i].groundNormal,car->wheels[i].width,gSurfaceTypes->types[phys->wheels[i].surfaceType].trackTexture,tracks,phys->wheels[i].lastTrack); + } + else phys->wheels[i].lastTrack=0; + } + } + + //make the car dirtier + phys->dirt+=totalSlip*kFrameTime*0.1; + if(phys->dirt>100) + phys->dirt=100; + //exhaust fire + if(car->exhaustFire+(phys->damage>kEngineDamage?100000:0)) + { + float exhaustFire=(((phys->oldThrottle-phys->throttle)*kFPS*phys->rpm)-(car->exhaustFire+(phys->damage>kEngineDamage?100000:0)))/((car->exhaustFire+(phys->damage>kEngineDamage?100000:0))); + if(exhaustFire>0) + { + if(exhaustFire>1)exhaustFire=1; + CarPlayCrashNoise(carEntity,FileGetReference("exhaust.wav"),exhaustFire); + if(exhaustFire>0.5) + { + tParticlesDef def; + ParticlesInitDef(&def); + + def.sprite=FileGetReference("exhaustfire.pct"); + def.maxSpread=0.1; + def.maxVelo=0.2; + def.gravity=-0.2; + def.maxLife=0.3; + def.screenTexture=-1; + def.xSize=0.6;def.ySize=0.6; + def.grow=true; + tVector3 baseVelo=Vector(0,0,0); + //two exhaust pipes. Both may be at the same space to make it look like one. + ParticlesCreate(exhaustFire*200*kFrameTime,car->exhaust1Pos*carEntity->dir+carEntity->pos,baseVelo,&def); + ParticlesCreate(exhaustFire*200*kFrameTime,car->exhaust2Pos*carEntity->dir+carEntity->pos,baseVelo,&def); + } + } + } + phys->oldThrottle=phys->throttle; + + //exhaust smoke. + float v=~carEntity->velo; + if(v<10&&phys->damagethrottle; + def.sprite=FileGetReference("exhaustsmoke.pct"); + def.maxSpread=0.1; + def.maxVelo=0.2; + def.gravity=-0.8; + def.maxLife=2; + def.a=1-(v*0.1); + def.screenTexture=-1; + def.xSize=0.4;def.ySize=0.4; + def.grow=true; + tVector3 baseVelo=-*MatrixGetZVector(carEntity->dir)*smoke*2; + //two exhaust pipes. Both may be at the same space to make it look like one. + ParticlesCreate(smoke*40*kFrameTime,car->exhaust1Pos*carEntity->dir+carEntity->pos,baseVelo,&def); + ParticlesCreate(smoke*40*kFrameTime,car->exhaust2Pos*carEntity->dir+carEntity->pos,baseVelo,&def); + } + + if(phys->damage>kEngineDamage) + { + tParticlesDef def; + ParticlesInitDef(&def); + + float smoke=0.2+0.8*phys->throttle; + def.sprite=FileGetReference("exhaustsmoke.pct"); + def.maxSpread=0.2; + def.maxVelo=0.3; + def.gravity=-0.7; + def.maxLife=2; + def.a=1; + def.screenTexture=-1; + def.xSize=1.5;def.ySize=1.5; + def.grow=true; + tVector3 baseVelo=Vector(0,1,0); + //two exhaust pipes. Both may be at the same space to make it look like one. + ParticlesCreate(smoke*60*kFrameTime,Vector(0,1,2.3)*carEntity->dir+carEntity->pos,baseVelo,&def); + } +} + +void CarPhysicsEntitySimulation(tGameEntity *carEntity) +{ + tCarPhysics *phys=(tCarPhysics*)carEntity->physics; + tCarDefinition *car=&(phys->car); + tWheelCalcData wheels[kMaxWheels]; + + if(carEntity==gViewedEntity) + phys->regCode=RT3_GetLicenseCode(); + + //Wheels position and velocity + CalcWheelPositions(carEntity,car,phys,wheels); + + //wheels position relative to the ground + CalcWheelGroundOffsets(carEntity,car,phys,wheels); + + //wheels springs & suspension + CalcWheelSupension(carEntity,car,phys,wheels); + + if(carEntity->physicsMachine==kPhysicsRemote||gReplay) + { + for(int i=0;imass*gEnvironment->gravity,0); + + //Apply Forces + tVector3 totalForce=totalWheelForce+gravityForce; + tVector3 acceleration=totalForce*(1/car->mass); + carEntity->velo=carEntity->velo+acceleration*kFrameTime; + } + else + { + //Force the wheel projects onto the road + CalcWheelRoadForce(carEntity,car,phys,wheels); + + //Sum of wheel forces + tVector3 totalWheelForce,totalWheelTorque; + CalcTotalWheelForce(carEntity,car,phys,wheels,&totalWheelForce,&totalWheelTorque); + + //Aerodynamics + tVector3 aerodynamicForce,aerodynamicTorque; + CalcAerodynamics(carEntity,car,&aerodynamicForce,&aerodynamicTorque); + + //Gravity + tVector3 gravityForce=Vector(0,-car->mass*gEnvironment->gravity,0); + + //Apply Forces + tVector3 totalForce=totalWheelForce+aerodynamicForce+gravityForce; + tVector3 acceleration=totalForce*(1/car->mass); + carEntity->accel=acceleration; + carEntity->velo=carEntity->velo+acceleration*kFrameTime; + + //Apply Torques + tVector3 totalTorque=totalWheelTorque+aerodynamicTorque; + + tMatrix3 invMatrix; + MatrixTranspose(carEntity->dir,invMatrix); + totalTorque=totalTorque*invMatrix; + float zTorq=1; + if(gFrameCountinertia.x,totalTorque.y/car->inertia.y,zTorq*totalTorque.z/car->inertia.z)*carEntity->dir; + tMatrix3 accelerationMatrix; + RotationVectorToMatrix(angularAcceleration*kFrameTime*kFrameTime,accelerationMatrix); + MatrixMult(carEntity->rVelo,accelerationMatrix,carEntity->rVelo); + + //if(carEntity->physicsMachine==kPhysicsLocal&&!gReplay) + //{ + //Lights + CarLights(phys); + + int wheelOnGround=0; + for(int i=0;inumWheels;i++) + { + if(wheels[i].onGround) + wheelOnGround++; + phys->wheels[i].onGround=wheels[i].onGround; + } +// if(wheelOnGround>2)//no y network Accel when on ground to dampen spring motion on bumpy surfaces +// carEntity->accel.y=0; + + //Crash Recovery + if(((wheelOnGroundnumWheels&&wheelOnGround<4)||phys->collision>gFrameCount-10)&&phys->damagecrashTime>kFPS*5) + if(sqr(carEntity->velo)collision>gFrameCount-10&&sqr(carEntity->velo)crashTime=gFrameCount; + phys->stuckTime=gFrameCount; + } + } + else + phys->crashTime=gFrameCount; + + carEntity->lastActivity=gFrameCount; + + int wheelsOnGround=0; + for(int i=0;inumWheels;i++) + if(wheels[i].onGround) + wheelsOnGround++; + phys->onGround=wheelsOnGround>=2; + + } + + //Smoke and Tracks! + CarCreateEffects(carEntity,car,phys,wheels); +} + +#define kMaxCornerAccel 15 +#define kMaxEngineAccel 6 +#define kMaxBrakeAccel 8 +#define kEngineFactor 0.8 + +void CarPhysicsEntitySimple(tGameEntity *carEntity) +{ + tCarPhysics *phys=(tCarPhysics*)carEntity->physics; + + tVector3 velo=carEntity->velo; + + tVector3 rVelo=MatrixToEulerAngles(carEntity->rVelo); + rVelo.y=0; + EulerAnglesToMatrix(rVelo,carEntity->rVelo); + + CarPhysicsEntitySimulation(carEntity); + + carEntity->velo.x=(5*velo.x+carEntity->velo.x)/6.0; + carEntity->velo.z=(5*velo.z+carEntity->velo.z)/6.0; + + tCarDefinition *car=&(phys->car); + + float groundFriction=0; + for(int i=0;inumWheels;i++) + groundFriction+=gSurfaceTypes->types[phys->wheels[i].surfaceType].grip/(float)car->numWheels; + + //Calculate the drivetrain ratio + float ratio=car->gearRatios[phys->gear+1]*car->finalDriveRatio; + + //Calculate the engine inertia at the final drive + float inertia=car->engineInertia*sqr(ratio); + + //Calculate the frictional braking torque of the engine (in N*m) + float engineFrictionTorque=25+phys->rpm*0.02; + if(car->engineBaseFriction||car->engineRPMFriction) + engineFrictionTorque=car->engineBaseFriction+phys->rpm*car->engineRPMFriction; + + //Calculate the current engine torque (in N*m) + float engineTorque=(CalcTorque(phys->rpm,car)+engineFrictionTorque)*phys->throttle-engineFrictionTorque; + + if(engineTorque<0&&carEntity->velo**MatrixGetZVector(carEntity->dir)*ratio<0) + engineTorque*=-1; + + tVector3 flatDir=!Vector(MatrixGetZVector(carEntity->dir)->x,0,MatrixGetZVector(carEntity->dir)->z); + tVector3 flatVelo=Vector(velo.x,0,velo.z); + + float fEngineForce=(car->aiSpeedIndex!=0?car->aiSpeedIndex:1)*kEngineFactor*engineTorque*ratio/car->wheels[2].radius; + if(fEngineForce>car->mass*kMaxEngineAccel*groundFriction) + fEngineForce=car->mass*kMaxEngineAccel*groundFriction; + tVector3 engineForce=flatDir*fEngineForce; + + float arcadeDraftBoost=CalcDraftFactor(carEntity)*(~carEntity->velo)/70.0; + + if(phys->arcadeDraftBoostarcadeDraftBoost+=2*kFrameTime; + if(phys->arcadeDraftBoost>arcadeDraftBoost) + phys->arcadeDraftBoost-=2*kFrameTime; + + if(phys->arcadeDraftBoost>1)phys->arcadeDraftBoost=1; + if(phys->arcadeDraftBoost<0)phys->arcadeDraftBoost=0; + + if(carEntity->controlType==kControlTypeAIInput) + phys->arcadeDraftBoost=0; + + //engineForce=engineForce+!engineForce*14*car->mass*phys->arcadeDraftBoost; + + tVector3 airRes=-carEntity->velo*~carEntity->velo*0.5*car->frontAirResistance*gEnvironment->airDensity; + + float surfaceBrake=0; + for(int i=0;inumWheels;i++) + surfaceBrake+=gSurfaceTypes->types[phys->wheels[i].surfaceType].brakeFactor*fabs(phys->wheels[i].angularVelo)/(float)car->numWheels; + float brake=phys->arcadeBrake+phys->handbrake*0.5; + if(brake>1)brake=1; + brake+=surfaceBrake; + tVector3 brakeForce=-!flatVelo*brake*car->mass*kMaxBrakeAccel; + if(sqr(brakeForce/car->mass)*kFrameTime>sqr(flatVelo)) + brakeForce=-!flatVelo*car->mass*kFrameTime; + + + tVector3 totalForce; + if(phys->onGround) + totalForce=engineForce+airRes+brakeForce; + else + totalForce=airRes; + tVector3 totalAccel=totalForce/car->mass; + if(gGameInfo->arcade==kGameModeTurbo) + totalAccel=totalAccel*2.3; + + carEntity->velo=carEntity->velo+(totalAccel*kFrameTime); + + tMatrix3 m; + MatrixIdentity(m); + MatrixRotY(m,phys->steering*0.1); + + flatDir=flatDir*~flatVelo; + flatDir=flatDir*m; + float rev=sign(flatDir*flatVelo); + flatDir=flatDir*rev; + float diff=~(flatVelo-flatDir); + float cornerAccel=kFrameTime*kMaxCornerAccel*groundFriction; + if(gGameInfo->arcade==kGameModeTurbo) + cornerAccel*=1.9; + if(cornerAccel>diff)cornerAccel=diff; + + tVector3 vCornerAccel=-!(flatVelo-flatDir)*cornerAccel; + carEntity->velo=carEntity->velo+vCornerAccel; + carEntity->accel=totalAccel+vCornerAccel*kFPS; + + float handBrakeFactor=1+phys->handbrake*2; + if(rev<0) + handBrakeFactor=1; + float veloSteer=((15/(~velo+1))+1)*handBrakeFactor; + float maxRotSteer=10*kFrameTime*handBrakeFactor; + float acardeSteerVeloInput=rev*phys->steering*~velo*kFrameTime*0.1*handBrakeFactor; + if(!phys->onGround) + acardeSteerVeloInput=0; + if(acardeSteerVeloInput>phys->arcadeSteerVelo){ + phys->arcadeSteerVelo+=0.015*veloSteer*kFrameTime; + if(acardeSteerVeloInputarcadeSteerVelo) + phys->arcadeSteerVelo=acardeSteerVeloInput; + } + else{ + phys->arcadeSteerVelo-=0.015*veloSteer*kFrameTime; + if(acardeSteerVeloInput>phys->arcadeSteerVelo) + phys->arcadeSteerVelo=acardeSteerVeloInput; + } + if(fabs(phys->arcadeSteerVelo)>maxRotSteer) + phys->arcadeSteerVelo=sign(phys->arcadeSteerVelo)*maxRotSteer; + MatrixRotY(carEntity->rVelo,phys->arcadeSteerVelo); +} + + +void CarPhysicsEntityMagic(tGameEntity *carEntity) +{ + tCarPhysics *phys=(tCarPhysics*)carEntity->physics; + + MatrixIdentity(carEntity->rVelo); + + tVector3 x[4],a[4]; + x[0]=Vector(1.2,0,2); + x[1]=Vector(-1.2,0,2); + x[2]=Vector(1.2,0,-2); + x[3]=Vector(-1.2,0,-2); + tVector3 howerAccel=Vector(0,0,0); + tVector3 howerRotAccel=Vector(0,0,0); + for(int i=0;i<4;i++) + { + x[i]=x[i]*carEntity->dir; + tVector3 normal; + float offs=GetGroundOffset(x[i]+carEntity->pos,&carEntity->lastRoadIndex,&normal,NULL); + if(offs<0.2)offs=0.2; + a[i]=normal*(1.25/offs)*(1+0.5*phys->steering*(i&1?1:-1))*(1+0.1*phys->brake*(i&2?1:-1))*(1-0.1*phys->throttle*(i&2?1:-1)); + howerAccel=howerAccel+a[i]; + howerRotAccel=howerRotAccel+x[i]%a[i]; + } + howerAccel=howerAccel*(1+sin(gFrameCount*kFrameTime*2)*0.12); + + tMatrix3 invMatrix; + MatrixTranspose(carEntity->dir,invMatrix); + howerRotAccel=Vector(howerRotAccel.x*5,0,howerRotAccel.z*30); + tMatrix3 accelerationMatrix; + RotationVectorToMatrix(howerRotAccel*kFrameTime*kFrameTime,accelerationMatrix); + MatrixMult(carEntity->rVelo,accelerationMatrix,carEntity->rVelo); + + tVector3 velo=carEntity->velo; + tVector3 gravityAccel=Vector(0,-gEnvironment->gravity,0); + tVector3 engineAccel=phys->throttle**MatrixGetZVector(carEntity->dir)*(20); + tVector3 brakeAccel=phys->brake*!(*MatrixGetZVector(carEntity->dir)+((sqr(carEntity->velo)>0.001)?carEntity->velo*0.1:Vector(0,0,0)))*-(22); + if(gFrameCount*kFrameTime<=kStartGameDelaySeconds) + { + engineAccel=Vector(0,0,0); + brakeAccel=Vector(0,0,0); + } + if(phys->lapCount>gGameInfo->numLaps&&gGameInfo->numLaps!=-1) + brakeAccel=phys->brake*(((sqr(carEntity->velo)>0.001)?!carEntity->velo:Vector(0,0,0)))*-(22); + brakeAccel.y=0; + tVector3 dampAccel=Vector(0,0,0); + tVector3 airResAccel=Vector(0,0,0); + if(sqr(carEntity->velo)>0.001) + { + dampAccel=-!carEntity->velo*5; + airResAccel=-!carEntity->velo*sqr(carEntity->velo)*0.003; + if(sqr(dampAccel*kFrameTime)>sqr(carEntity->velo)) + dampAccel=-carEntity->velo*kFPS; + dampAccel.x*=0.1; + dampAccel.z*=0.1; + } + tVector3 totalAccel=gravityAccel+howerAccel+engineAccel+brakeAccel+dampAccel+airResAccel; + + carEntity->velo=carEntity->velo+totalAccel*kFrameTime; + carEntity->lastActivity=gFrameCount; + + tVector3 flatDir=!Vector(MatrixGetZVector(carEntity->dir)->x,0,MatrixGetZVector(carEntity->dir)->z); + tVector3 flatVelo=Vector(velo.x,0,velo.z); + + tMatrix3 m; + MatrixIdentity(m); + MatrixRotY(m,phys->steering*0.1); + + flatDir=flatDir*~flatVelo; + flatDir=flatDir*m; + float rev=sign(flatDir*flatVelo); + flatDir=flatDir*rev; + float diff=~(flatVelo-flatDir); + + float cornerAccel=kFrameTime*kMaxCornerAccel; + if(gGameInfo->arcade==kGameModeTurbo) + cornerAccel*=1.9; + if(cornerAccel>diff)cornerAccel=diff; + + tVector3 vCornerAccel=-!(flatVelo-flatDir)*cornerAccel; + carEntity->velo=carEntity->velo+vCornerAccel; + carEntity->accel=totalAccel+vCornerAccel*kFPS; + + float acardeSteerVeloInput=phys->steering*kFrameTime*(3+~velo*0.04); + if(acardeSteerVeloInput>phys->arcadeSteerVelo){ + phys->arcadeSteerVelo+=0.15*kFrameTime; + if(acardeSteerVeloInputarcadeSteerVelo) + phys->arcadeSteerVelo=acardeSteerVeloInput; + } + else{ + phys->arcadeSteerVelo-=0.15*kFrameTime; + if(acardeSteerVeloInput>phys->arcadeSteerVelo) + phys->arcadeSteerVelo=acardeSteerVeloInput; + } + if(gFrameCount*kFrameTime>=kStartGameDelaySeconds) + MatrixRotY(carEntity->rVelo,phys->arcadeSteerVelo); + + if(gEnvironment->spotLightEnable&&gFrameCount==0) + phys->lightFlags|=kLightFlagDriveLight; + + if(phys->collision>gFrameCount-10) + { + if(gFrameCount-phys->crashTime>kFPS*5) + if(sqr(carEntity->velo)crashTime=gFrameCount; + phys->stuckTime=gFrameCount; + } + }else + phys->crashTime=gFrameCount; + + *MatrixGetYVector(carEntity->dir)=!(*MatrixGetYVector(carEntity->dir)+(Vector(0,1,0)-*MatrixGetYVector(carEntity->dir))*kFrameTime*0.5); + *MatrixGetXVector(carEntity->dir)=!(*MatrixGetYVector(carEntity->dir)%*MatrixGetZVector(carEntity->dir)); + *MatrixGetZVector(carEntity->dir)=!(*MatrixGetXVector(carEntity->dir)%*MatrixGetYVector(carEntity->dir)); +} + + + +void CarPhysicsEntity(tGameEntity *carEntity) +{ + tCarPhysics *phys=(tCarPhysics*)carEntity->physics; + tCarDefinition *car=&(phys->car); + if(car->magic) + CarPhysicsEntityMagic(carEntity); + else if((gGameInfo->arcade==kGameModeTurbo||gGameInfo->arcade==kGameModeArcade||carEntity->controlType==kControlTypeAIInput)&&carEntity->physicsMachine==kPhysicsLocal) + CarPhysicsEntitySimple(carEntity); + else + CarPhysicsEntitySimulation(carEntity); +} +#define kPredictTime 0.15 +//Move the car around + +void MatrixReAdjust2(tMatrix3 m) +{ + *(tVector3*)m[1]=!*(tVector3*)m[1]; + *(tVector3*)m[0]=!(*(tVector3*)m[1]%*(tVector3*)m[2]); + *(tVector3*)m[2]=!(*(tVector3*)m[0]%*(tVector3*)m[1]); +} + +void FullyResetCar(tGameEntity *carEntity) +{ + tCarPhysics *phys=(tCarPhysics*)carEntity->physics; + carEntity->pos=gMapInfo->startPos+Vector(0,10,0); + carEntity->oldPos=carEntity->pos; + carEntity->netPos=carEntity->pos; + carEntity->velo=Vector(0,0,0); + carEntity->netVelo=Vector(0,0,0); + carEntity->collVelo=Vector(0,0,0); + carEntity->accel=Vector(0,0,0); + MatrixIdentity(carEntity->dir); + MatrixIdentity(carEntity->rVelo); + MatrixIdentity(carEntity->oldDir); + MatrixIdentity(carEntity->netDir); + MatrixIdentity(carEntity->netRVelo); + for(int i=0;ilastRVelos[i]); + carEntity->lastVelos[i]=Vector(0,0,0); + carEntity->lastAccel[i]=Vector(0,0,0); + } + carEntity->remoteCameraPos=Vector(0,0,0); + carEntity->zDist=0; + phys->maxSpeed=0; + phys->averageSpeed=0; + phys->accel100=0; + phys->accel200=0; + phys->accelQuarter=0; + phys->accelKM=0; + phys->odo; + phys->position=0; + phys->lead=0; + phys->rpm=0; + phys->engineAngularVelo=0; + phys->drivetrainAngularVelo=0; + phys->clutchTorque=0; + for(int i=0;iwheels[i].angle=0; + phys->wheels[i].suspension=0; + phys->wheels[i].slipVelo=0; + phys->wheels[i].slip=0; + phys->wheels[i].slipAngle=0; + phys->wheels[i].rotation=0; + phys->wheels[i].angularVelo=0; + phys->wheels[i].glow=0; + } +} + + +void CarMotionEntity(tGameEntity *carEntity) +{ + tCarPhysics *phys=(tCarPhysics*)carEntity->physics; + tCarDefinition *car=&(phys->car); + + for(int i=kNumAvgRVelos-2;i>=0;i--) + { + MatrixCopy(carEntity->lastRVelos[i],carEntity->lastRVelos[i+1]); + carEntity->lastVelos[i+1]=carEntity->lastVelos[i]; + carEntity->lastAccel[i+1]=carEntity->lastAccel[i]; + } + MatrixCopy(carEntity->rVelo,carEntity->lastRVelos[0]); + carEntity->lastVelos[0]=carEntity->velo; + carEntity->lastAccel[0]=carEntity->accel; + + + carEntity->lastActivity=gFrameCount; + //move the car + + if(carEntity->physicsMachine==kPhysicsRemote) + { + if(!gReplay) + { + float smoothness=0; + for(int i=0;ilastCollisions[i].frameCount)*kFrameTime<0.5) + smoothness+=(8.0f/kNumLastCollisions); + if(smoothness>1) + smoothness=1; + float predictTime=kPredictTime*(1+smoothness*1.5); + carEntity->netVelo=carEntity->netVelo+carEntity->accel*kFrameTime; + carEntity->netPos=carEntity->netPos+carEntity->netVelo*kFrameTime; + carEntity->velo=carEntity->velo+((carEntity->netPos+carEntity->netVelo*predictTime+carEntity->accel*predictTime*predictTime*0.5)-(carEntity->pos+carEntity->velo*predictTime))/predictTime; + MatrixMult(carEntity->netDir,carEntity->rVelo,carEntity->netDir); + tVector3 netx=*MatrixGetXVector(carEntity->netDir); + tVector3 nety=*MatrixGetYVector(carEntity->netDir); + tVector3 netz=*MatrixGetZVector(carEntity->netDir); + tVector3 curx=*MatrixGetXVector(carEntity->dir); + tVector3 curz=*MatrixGetZVector(carEntity->dir); + + *MatrixGetXVector(carEntity->dir)=curx+(netx-curx)*0.3; + *MatrixGetYVector(carEntity->dir)=nety; + *MatrixGetZVector(carEntity->dir)=curz+(netz-curz)*0.3; + MatrixReAdjust2(carEntity->dir); + + if(sqr(carEntity->pos-carEntity->netPos)>sqr(5+smoothness*10)) + { + carEntity->pos=carEntity->netPos; + carEntity->velo=carEntity->netVelo; + MatrixCopy(carEntity->netDir,carEntity->dir); + } + } + for(int i=0;inumWheels;i++) + phys->wheels[i].rotation+=phys->wheels[i].angularVelo*kFrameTime; + } + if(gReplay) + { + carEntity->velo=carEntity->velo+carEntity->accel*kFrameTime; + MatrixMult(carEntity->netDir,carEntity->rVelo,carEntity->netDir); + tVector3 netx=*MatrixGetXVector(carEntity->netDir); + tVector3 nety=*MatrixGetYVector(carEntity->netDir); + tVector3 netz=*MatrixGetZVector(carEntity->netDir); + tVector3 curx=*MatrixGetXVector(carEntity->dir); + tVector3 curz=*MatrixGetZVector(carEntity->dir); + + *MatrixGetXVector(carEntity->dir)=curx+(netx-curx)*0.3; + *MatrixGetYVector(carEntity->dir)=nety; + *MatrixGetZVector(carEntity->dir)=curz+(netz-curz)*0.3; + MatrixReAdjust2(carEntity->dir); + } + carEntity->pos=carEntity->pos+carEntity->velo*kFrameTime; + + + //rotate the car around it's center of gravity + tVector3 massCenter=car->massCenter*carEntity->dir; + tVector3 massMotion=((massCenter*carEntity->rVelo)-massCenter); + MatrixMult(carEntity->dir,carEntity->rVelo,carEntity->dir); + carEntity->pos=carEntity->pos-massMotion; + + //update road position index + float oldPosition=phys->position; + phys->position=RoadGetPosition(carEntity->pos,carEntity->lastRoadIndex,NULL); + + if(carEntity==gViewedEntity) + for(int i=0;ireverse){ + if(phys->positiongCornerSigns[i].pos) + { + gAccelSignDisplayIntensity=1.5; + gAccelSignDisplayCorner=-gCornerSigns[i].accel; + } + }else{ + if(phys->position>gCornerSigns[i].pos&&oldPositionreverse) + { + if(oldPosition>phys->position+50) + phys->lap--; + else if(oldPositionposition-50) + phys->lap++; + } + else + { + if(oldPosition>phys->position+50) + phys->lap++; + else if(oldPositionposition-50) + phys->lap--; + } + + //test if we are stuck (used for ai recovery) + int stuck=gFrameCount*kFrameTime>5&&sqr(carEntity->velo)lapCount<=gGameInfo->numLaps; + if(stuck) + phys->stuckTime++; + else + phys->stuckTime=0; + + //are we moving backwards? (used to display "wrong direction" message). + if((gGameInfo->reverse?oldPosition<=phys->position:oldPosition>=phys->position)&&sqr(carEntity->velo)>sqr(5)) + phys->wrongDriectionFrames++; + else + phys->wrongDriectionFrames-=10; + if(phys->wrongDriectionFrames<0) + phys->wrongDriectionFrames=0; + else if(phys->wrongDriectionFrames>kFPS*5) + { + phys->wrongDriectionFrames=kFPS*5; + if(gGameInfo->network&kGameInfoNetworkCantGoBackwards) + { + if(carEntity==gViewedEntity) + TextPrintfToBufferFormatedFading(Vector(kFadingMessageXPos,kFadingMessageYPos),kFadingMessageSize,kTextAlignMiddle,0.9,1.8,"Wrong Way!"); + RoadCenterCar(carEntity); + } + } + + float velo=~carEntity->velo; + //Check if we have beaten our speed record for this game + if(velo>phys->maxSpeed) + phys->maxSpeed=velo; + + //Update our average speed + if(phys->lapCount&&(phys->lapCount<=gGameInfo->numLaps||gGameInfo->numLaps==-1)&&!gReplay) + { + phys->averageSpeed=((phys->averageSpeed*(phys->averageSpeedFrames))+velo)/(phys->averageSpeedFrames+1); + phys->averageSpeedFrames++; + } + if(velo>=100/3.6&&phys->accel100==0) + phys->accel100=gFrameCount*kFrameTime-kStartGameDelaySeconds; + if(velo>=200/3.6&&phys->accel200==0) + phys->accel200=gFrameCount*kFrameTime-kStartGameDelaySeconds; + phys->odo+=velo*kFrameTime; + if(phys->odo>=402.32454&&phys->accelQuarter==0) + phys->accelQuarter=gFrameCount*kFrameTime-kStartGameDelaySeconds; + if(phys->odo>=1000&&phys->accelKM==0) + phys->accelKM=gFrameCount*kFrameTime-kStartGameDelaySeconds; + /*if(Button()&&phys->accel200!=0&&!gReplay) + { + printf("0-100km/h: %.1f seconds\n0-200km/h: %.1f seconds\n100-200km/h: %.1f seconds\nQuarter Mile: %.1f seconds\n1km: %.1f seconds\nTop Speed:%.1f km/h\n",phys->accel100,phys->accel200,phys->accel200-phys->accel100,phys->accelQuarter,phys->accelKM,phys->maxSpeed*3.6); + phys->accel200=0; + }*/ + if(isnan(carEntity->pos.x)||isnan(carEntity->pos.y)||isnan(carEntity->pos.z)) + FullyResetCar(carEntity); +} \ No newline at end of file diff --git a/source/carphysics.h b/source/carphysics.h new file mode 100755 index 0000000..ef6aee2 --- /dev/null +++ b/source/carphysics.h @@ -0,0 +1,214 @@ +#ifndef __CARPHYSICS +#define __CARPHYSICS + +#include "fileio.h" +#include "vectors.h" +#include "entities.h" +#include "gameinitexit.h" + +#define kMaxWheels 6 + +enum{ + kLightFlagDriveLight=1<<0, + kLightFlagFogLight=1<<2, + kLightFlagRevLight=1<<3, + kLightFlagBrakeLight=1<<4, + kLightFlagLIndLight=1<<5, + kLightFlagRIndLight=1<<6, + kLightFlagHorn=1<<7 +}; + +typedef struct{ + tVector3 pos; + int texture; + float powered; + float maxAngle; + float maxSuspension; //m + float radius; //m + float width; + float loadSensitivity; + float stickyness; + float grip; + float rollCenter; + float braked,handbraked; + float tolerance; + float inertia; + float tilt; + float friction; + tFileRef model; + tFileRef customBrakeModel; + int noShadow; +} tWheelDefinition; + +#define kFatalDamage 400 +#define kEngineDamage 300 +#define kSuspensionDamage 200 + + +typedef struct{ + char name[64]; + char describtion[256]; + int requirements,conflicts; + float mass; + float frontLift,rearLift; + float frontMaxSuspension,rearMaxSuspension; + float frontWheelWidth,rearWheelWidth; + float frontAirResistance; + float powerPercent,torquePercent; + float finalDriveRatio,topGearRatio; + float engineInertia; + float gearSwitchTime; + float differentialLockCoefficient; + float frontSwayBar,rearSwayBar; + float track; + float maxRPM; + float exhaustFire; + float damperStrength; + tVector3 massCenter; + int hasGraphic; + tFileRef model; + int price; + int group; +} tAddOnDefinition; + +typedef struct{ + float frontAirResistance,sideAirResistance,topAirResistance; + float frontLift,rearLift; + float mass; //kg + float power; //W + float torque; //Nm + float powerRPM,torqueRPM; //revs per minute + float clutchRPM; + float idleRPM,jerkRPM,maxRPM; + float shiftUpRPM,shiftDownRPM,shiftUpRPMFix; + float finalDriveRatio; + float differentialLockCoefficient; + float supsensionFriction,damperStrength; + float engineInertia; + float engineFriction,engineBaseFriction,engineRPMFriction; + float maxClutchTorqueTransfer; + float zeroRPMSoundGain,fullRPMSoundGain,zeroThrottleSoundGain,fullThrottleSoundGain; + float zeroRPMSoundPitch,fullRPMSoundPitch,zeroThrottleSoundPitch,fullThrottleSoundPitch; + float gearSwitchTime; + float exhaustFire; + float frontSwayBar,rearSwayBar; + float aiSpeedIndex; + int numGears,numWheels,numLights,numAddOns; + int numColors; + int initialAddOns; + int challengeRequirements; + int demoAvailable,builtIn; + tVector3 massCenter; + tVector3 inertia; + tVector3 steeringWheelPos; + tVector3 driverPos; + tVector3 exhaust1Pos,exhaust2Pos; + tVector3 frontLicensePlatePos,rearLicensePlatePos; + float steeringWheelAngle,steeringWheelTurns,steeringWheelRadius; + tFileRef steeringWheelTexture; + tFileRef model,interiorModel,shadowModel; + tFileRef engineSample,hallSample,turboSample,hornSample; + float hornPitch; + int numCollBoxes; + int noDriverModel; + float maxCollRadius; + float *gearRatios; + tWheelDefinition *wheels; + tLightDefinition *lights; + tAddOnDefinition *addOns; + tVector3 *colors; + int *colorLoaded; + tCollBox *coll; + char carName[64]; + int year; + int magic; + int secret; + int price; + float displacement; + float turboGain; +} tCarDefinition; + +typedef struct{ + float angle; + float suspension; //m + float slipVelo,slip,slipAngle; + float rotation; + float angularVelo; + float glow; + int lastTrack; + int onGround; + int surfaceType; +} tWheelPhysics; + +#define kNumLastCollisions 32 +typedef struct{ + int frameCount; + tVector3 attackPoint; + tVector3 veloDiff; + float rotationFactor; +} tCollisionStruct; + +typedef struct{ + tCarDefinition car; + int addOns; //flags for add-ons installed. + int color; + float aiPowerCycle; //for AI cars: start of AI power cycle which makes AI cars behave more randomly + float aiRouteCycle; + int gear; //current gear car is in + int lastGear; + int lightFlags; //which lights are on + float echo; //is the car in a tunnel? + int lap; //internal lap counter for position determination (not neccesarily ==lapCount) + int lapCount; //lap counter + int wrongDriectionFrames; //number of Frames the car is moving into the wrong Direction + int lapTimes[kMaxLaps+1]; //frame Count each time the finish line was crossed + int finishTime; //frame Count the race was finished or is prognosed to be finished + int lappedPlayers[kMaxPlayers]; + int averageSpeedFrames; //number of frames used to calculate average speed so far. + float maxSpeed,averageSpeed,accel100,accel200,accelQuarter,accelKM,odo; + float position; + int checkPoint; + float lead; + float rpm; + float engineAngularVelo,drivetrainAngularVelo; + float clutchTorque; + float throttle,oldThrottle,steering,brake,arcadeBrake,handbrake,clutch; + float idleThrottle; + float lastGearSwitch; + float arcadeSteerVelo,arcadeDraftBoost; + float maxSlip,maxAngle; + float noisePriority; + float dirt; + int crashTime; //the last frame in which all four wheels have been on the ground. + int stuckTime; //the last frame in which the car seemed to make some progress (ai) + int collision; //has the car collided with something solid this frame? (for getting stuck detection) + int onGround; + tGameEntity *overtaking; + int overtakeSide; + float overTakeProgress; + tVector3 lastSamplePos; + float dirtStretch[5]; + tWheelPhysics wheels[kMaxWheels]; + UInt64 regCode; + char *plateName; + float damage; + float turboRPM; + tCollisionStruct lastCollisions[kNumLastCollisions]; +} tCarPhysics; + +typedef struct{ + tVector3 pos,velo,groundNormal; + tVector3 roadForce; + int onGround; + float bump; + float inertia; + float oldAngle; + float normalForce,suspensionForce; +} tWheelCalcData; + +void InstallCarAddOns(tCarPhysics *phys); +float CalcDraftFactor(tGameEntity *car1); +void CarPhysicsEntity(tGameEntity *carEntity); +void CarMotionEntity(tGameEntity *carEntity); + +#endif \ No newline at end of file diff --git a/source/carselection.cpp b/source/carselection.cpp new file mode 100644 index 0000000..967c674 --- /dev/null +++ b/source/carselection.cpp @@ -0,0 +1,819 @@ +#include "stdtypes.h" +#include "reg_tool_3.h" +#include "rt3_redline.h" + + +#include +#include +#include +#include "gametime.h" +#include "gamemem.h" +#include "fileio.h" +#include "rendercar.h" +#include "carphysics.h" +#include "screen.h" +#include "controls.h" +#include "environment.h" +#include "transparency.h" +#include "renderframe.h" +#include "text.h" +#include "interface.h" +#include "gameinitexit.h" +#include "carselection.h" +#include "config.h" +#include "interfaceutil.h" +#include "stencil.h" +#include "gamesystem.h" +#include "textures.h" +#include "challenges.h" +#include "gamesound.h" +#include "random.h" + +#define REGISTERED (RT3_IsRegistered()) + +char *StripName(char *aName); + +int CompareCars(const void *a,const void *b) +{ + tCarDefinition *cara=(tCarDefinition*)FileGetParsedDataPtr(*(tFileRef*)a,kParserTypeCarDesc,sizeof(tCarDefinition)); + tCarDefinition *carb=(tCarDefinition*)FileGetParsedDataPtr(*(tFileRef*)b,kParserTypeCarDesc,sizeof(tCarDefinition)); + if(!REGISTERED) + if(cara->demoAvailable!=carb->demoAvailable) + return carb->demoAvailable-cara->demoAvailable; + return _stricmp(StripName(cara->carName),StripName(carb->carName)); +} + +void GetAvailableCars(tFileRef *availableCars,int *carCount,int onlyBuiltIn,int onlyAvailable) +{ + *carCount=0; + for(int i=0;ibuiltIn||!onlyBuiltIn)&&HasChallengeRequirements(car->challengeRequirements,gConfig->challengeData)) + availableCars[(*carCount)++]=i; + if(car->shiftUpRPMmaxRPM*0.8) + printf("%s: %f/%f\n",car->carName,car->shiftUpRPM,car->maxRPM); + } + if(!onlyAvailable) + for(int i=0;ibuiltIn||!onlyBuiltIn)&&!HasChallengeRequirements(car->challengeRequirements,gConfig->challengeData)&&!car->secret) + availableCars[(*carCount)++]=i; + } + qsort(availableCars,*carCount,sizeof(tFileRef),CompareCars); +} + +#define kSpecsXPos 0.9 +#define kSpecsYPos 0.6 +#define kSpecsSize 0.03 +#define kSpecsOffset 0.07 +#define kSpecsAlign kTextAlignRight + +//#define kColorsXPos 0.5 +//#define kColorsYPos 0.2 +#define kColorsXPos 0.42 +#define kColorsYPos -0.3 +#define kColorsWidth 0.04 +#define kColorsHeight 0.02 +//#define kColorsOffset 0.025 +#define kColorsOffset 0.05 +#define kPointerSize 0.03 + +void CarSelectionDrawBackground(int mode) +{ + char title[256]; + + switch(mode) + { + case kCarSelectionQuickMode: + strcpy(title,"Select Car:"); + break; + + case kCarSelectionEnemyMode: + strcpy(title,"Select Opponent Car:"); + break; + + case kCarSelectionDisplayMode: + strcpy(title,"New Car Unlocked!"); + break; + + } + InterfaceDrawBackground(-1,-1,0,0,0,0,1); + TextPrintfToBufferFormated(Vector(kTitlePosX,kTitlePosY),0.08,kTextAlignLeft,title); +} + +void CarSelectionDrawCar(tFileRef carRef,float time,int addOns,int color) +{ + tGameEntity carEntity,cameraEntity; + tCarPhysics *phys=(tCarPhysics*)MemoryAllocateZeroedBlock(sizeof(tCarPhysics)); + cameraEntity.pos=Vector(0,1.5,0); + carEntity.pos=Vector(0,0,7); + *MatrixGetZVector(cameraEntity.dir)=!(carEntity.pos-cameraEntity.pos); + *MatrixGetYVector(cameraEntity.dir)=Vector(0,1,0); + MatrixReAdjust(cameraEntity.dir); + carEntity.pos.x-=0.5; + carEntity.pos.y-=1.0; + MatrixIdentity(carEntity.dir); + MatrixRotY(carEntity.dir,time*PI/4); + + carEntity.renderType=kRenderTypeCar; + carEntity.physicsType=kPhysicsTypeCar; + carEntity.physicsData=carRef; + carEntity.physics=phys; + + tCarDefinition *car=(tCarDefinition*)FileGetParsedDataPtr(carRef,kParserTypeCarDesc,sizeof(tCarDefinition)); + phys->car=*car; + phys->car.wheels=(tWheelDefinition*)MemoryAllocateBlock(sizeof(tWheelDefinition)*car->numWheels); + MemoryMove(phys->car.wheels,car->wheels,sizeof(tWheelDefinition)*car->numWheels); + phys->addOns=addOns; + phys->color=color; + InstallCarAddOns(phys); + car=&phys->car; + + for(int i=0;inumWheels;i++) + phys->wheels[i].suspension=car->wheels[i].maxSuspension/2; + + tGameEntity *oldCameraEntity=gCameraEntity; + gCameraEntity=&cameraEntity; + SetupTranslation(carEntity.pos,carEntity.dir); + glColorMask(0,0,0,0); + glBegin(GL_QUADS); + glVertex3f(-100,0,100); + glVertex3f(100,0,100); + glVertex3f(100,0,-100); + glVertex3f(-100,0,-100); + glEnd(); + glColorMask(1,1,1,1); + + carEntity.pos.y=-1-car->wheels->pos.y+car->wheels->maxSuspension/2+car->wheels->radius; + SetupTranslation(carEntity.pos,carEntity.dir); + + glPushAttrib(GL_LIGHTING_BIT+GL_POLYGON_BIT+GL_COLOR_BUFFER_BIT+GL_TEXTURE_BIT); + CarRenderEntity(&carEntity,gConfig->interiorDisplay?true:false,false); + glPopAttrib(); + + for(int i=1;i<=gConfig->stencil;i++) + { + gStencilZoom=0.9+0.1*i/(float)gConfig->stencil; + SetupTranslation(carEntity.pos,carEntity.dir); + glPushAttrib(GL_ENABLE_BIT+GL_POLYGON_BIT); + CarRenderEntityShadow(&carEntity,6); + glPopAttrib(); + RenderStencilLayer(true,gConfig->stencil); + } + DrawTransparentPolys(NULL); + + MemoryFreeBlock(phys->car.wheels); + MemoryFreeBlock(phys); + + gCameraEntity=oldCameraEntity; + +} + +void CarSelectionDrawSpecs(tFileRef *availableCars,int numAvailable,int selected,int mode,int *available) +{ + //if(fadeName) + //gTextOpacity=1; + + for(int i=-3;i<=3;i++) + { + tCarDefinition *car=(tCarDefinition*)FileGetParsedDataPtr(availableCars[(selected+i+numAvailable)%numAvailable],kParserTypeCarDesc,sizeof(tCarDefinition)); + TextPrintfToBufferFormatedColored(Vector(-kSpecsXPos,0.4-i*0.1-fabs(i)*0.008),0.05-fabs(i)*0.008,kTextAlignLeft,1,1,1,1-fabs(i*0.15)-(i?0.3:0),car->carName); + } + tCarDefinition *car=(tCarDefinition*)FileGetParsedDataPtr(availableCars[selected],kParserTypeCarDesc,sizeof(tCarDefinition)); +// TextPrintfToBufferFormated(Vector(kSpecsXPos,0.5),0.05,kTextAlignLeft,car->carName); + + +// gTextOpacity=opacity; + float yPos=kSpecsYPos; + + if(car->magic) + { +// TextPrintfToBufferFormated(Vector(kSpecsXPos,yPos-5*kSpecsOffset),kSpecsSize,kSpecsAlign,"Where we're going, we don't need specifications."); + TextPrintfToBufferFormated(Vector(kSpecsXPos,yPos-0*kSpecsOffset),kSpecsSize,kSpecsAlign,"Where we're going, we don't need specifications."); + } + else + { + if(car->numWheels>=4) + if(car->wheels[0].powered&&car->wheels[2].powered) + TextPrintfToBufferFormated(Vector(kSpecsXPos,yPos-0.0),kSpecsSize,kSpecsAlign,"Drivetrain: \255#a\2554WD"); + else if(car->wheels[0].powered) + TextPrintfToBufferFormated(Vector(kSpecsXPos,yPos-0.0),kSpecsSize,kSpecsAlign,"Drivetrain: \255#a\255FWD"); + else if(car->wheels[2].powered) + TextPrintfToBufferFormated(Vector(kSpecsXPos,yPos-0.0),kSpecsSize,kSpecsAlign,"Drivetrain: \255#a\255RWD"); + + TextPrintfToBufferFormated(Vector(kSpecsXPos,yPos-5*kSpecsOffset),kSpecsSize,kSpecsAlign,"Gearbox: \255#a\255%d Gears",car->numGears-2); + if(gConfig->metricUnits) + { + TextPrintfToBufferFormated(Vector(kSpecsXPos,yPos-1*kSpecsOffset),kSpecsSize,kSpecsAlign,"Displacement: \255#a\255%1.1f L",car->displacement); + TextPrintfToBufferFormated(Vector(kSpecsXPos,yPos-2*kSpecsOffset),kSpecsSize,kSpecsAlign,"Mass: \255#a\255%4.0f kg",car->mass); + TextPrintfToBufferFormated(Vector(kSpecsXPos,yPos-3*kSpecsOffset),kSpecsSize,kSpecsAlign,"Power: \255#a\255%3.0f kw @ %4.0f RPM",car->power/1000,car->powerRPM); + TextPrintfToBufferFormated(Vector(kSpecsXPos,yPos-4*kSpecsOffset),kSpecsSize,kSpecsAlign,"Torque: \255#a\255%3.0f Nm @ %4.0f RPM",car->torque,car->torqueRPM); + } + else + { + TextPrintfToBufferFormated(Vector(kSpecsXPos,yPos-1*kSpecsOffset),kSpecsSize,kSpecsAlign,"Displacement: \255#a\255%3.0f cui",car->displacement*61.023744); + TextPrintfToBufferFormated(Vector(kSpecsXPos,yPos-2*kSpecsOffset),kSpecsSize,kSpecsAlign,"Mass: \255#a\255%4.0f lbs",car->mass*2.2046); + TextPrintfToBufferFormated(Vector(kSpecsXPos,yPos-3*kSpecsOffset),kSpecsSize,kSpecsAlign,"Power: \255#a\255%3.0f hp @ %4.0f RPM",car->power/745.69987,car->powerRPM); + TextPrintfToBufferFormated(Vector(kSpecsXPos,yPos-4*kSpecsOffset),kSpecsSize,kSpecsAlign,"Torque: \255#a\255%3.0f lb-ft @ %4.0f RPM",car->torque/1.35628105,car->torqueRPM); + } + } + if(!car->demoAvailable&&!REGISTERED&&mode!=kCarSelectionEnemyMode) + TextPrintfToBufferFormatedColored(Vector(0.2,0),0.15,kTextAlignMiddle,1,1,1,0.5,"DEMO"); +// TextPrintfToBufferFormatedColored(Vector(0.2,0.2),0.25,kTextAlignMiddle,1,1,1,0.8,"\255demo_car.png\255"); + else if(!HasChallengeRequirements(car->challengeRequirements,gConfig->challengeData)&&mode!=kCarSelectionEnemyMode) + TextPrintfToBufferFormatedColored(Vector(0.2,0),0.15,kTextAlignMiddle,1,1,1,0.5,"LOCKED"); + if(available) + *available=((car->demoAvailable||REGISTERED)&&HasChallengeRequirements(car->challengeRequirements,gConfig->challengeData))||mode==kCarSelectionEnemyMode; + gTextOpacity=1; +} + +void CarSelectionDrawColors(tFileRef carRef,int color) +{ +/* gTexturesQualityModifier=-255; + glPushAttrib(GL_COLOR_BUFFER_BIT+GL_CURRENT_BIT); + glDisable(GL_LIGHTING); + tCarDefinition *car=(tCarDefinition*)FileGetParsedDataPtr(carRef,kParserTypeCarDesc,sizeof(tCarDefinition)); + + glLoadIdentity(); + glTranslatef(0.0f,0.0f,-1.0f); + TexturesSelectTex(FileGetReference("colorbevel.pct")); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + + for(int i=0;inumColors;i++) + { + glColor3fv(&(car->colors[i].x)); + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex2f(kColorsXPos+kColorsWidth,kColorsYPos-kColorsHeight/2-i*kColorsOffset); + glTexCoord2d(1,0); glVertex2f(kColorsXPos+kColorsWidth,kColorsYPos+kColorsHeight/2-i*kColorsOffset); + glTexCoord2d(0,1); glVertex2f(kColorsXPos,kColorsYPos-kColorsHeight/2-i*kColorsOffset); + glTexCoord2d(0,0); glVertex2f(kColorsXPos,kColorsYPos+kColorsHeight/2-i*kColorsOffset); + glEnd(); + } + if(car->numColors) + { + if(color>=car->numColors) + color=car->numColors-1; + glColor3f(1,1,1); + TexturesSelectTex(FileGetReference("menupointer.tif")); + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex2f(kColorsXPos-kColorsWidth+kPointerSize,kColorsYPos-kPointerSize/2-color*kColorsOffset); + glTexCoord2d(1,0); glVertex2f(kColorsXPos-kColorsWidth+kPointerSize,kColorsYPos+kPointerSize/2-color*kColorsOffset); + glTexCoord2d(0,1); glVertex2f(kColorsXPos-kColorsWidth,kColorsYPos-kPointerSize/2-color*kColorsOffset); + glTexCoord2d(0,0); glVertex2f(kColorsXPos-kColorsWidth,kColorsYPos+kPointerSize/2-color*kColorsOffset); + glEnd(); + } + + glPopAttrib(); + gTexturesQualityModifier=0;*/ + + gTexturesQualityModifier=-255; + glPushAttrib(GL_COLOR_BUFFER_BIT+GL_CURRENT_BIT); + glDisable(GL_LIGHTING); + tCarDefinition *car=(tCarDefinition*)FileGetParsedDataPtr(carRef,kParserTypeCarDesc,sizeof(tCarDefinition)); + + glLoadIdentity(); + glTranslatef(0.0f,0.0f,-1.0f); + TexturesSelectTex(FileGetReference("colorbevel.pct")); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + + for(int i=0;inumColors;i++) + { + glColor3fv(&(car->colors[i].x)); + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex2f(kColorsXPos-i*kColorsOffset+kColorsWidth,kColorsYPos-kColorsHeight/2); + glTexCoord2d(1,0); glVertex2f(kColorsXPos-i*kColorsOffset+kColorsWidth,kColorsYPos+kColorsHeight/2); + glTexCoord2d(0,1); glVertex2f(kColorsXPos-i*kColorsOffset,kColorsYPos-kColorsHeight/2); + glTexCoord2d(0,0); glVertex2f(kColorsXPos-i*kColorsOffset,kColorsYPos+kColorsHeight/2); + glEnd(); + } + if(car->numColors) + { + if(color>=car->numColors) + color=car->numColors-1; + glColor3f(1,1,1); + TexturesSelectTex(FileGetReference("menupointer.tif")); + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(0,1); glVertex2f(kColorsXPos-color*kColorsOffset+kColorsWidth/2+kPointerSize/2,kColorsYPos-kColorsHeight*2); + glTexCoord2d(1,1); glVertex2f(kColorsXPos-color*kColorsOffset+kColorsWidth/2+kPointerSize/2,kColorsYPos-kColorsHeight*2+kPointerSize); + glTexCoord2d(0,0); glVertex2f(kColorsXPos-color*kColorsOffset+kColorsWidth/2-kPointerSize/2,kColorsYPos-kColorsHeight*2); + glTexCoord2d(1,0); glVertex2f(kColorsXPos-color*kColorsOffset+kColorsWidth/2-kPointerSize/2,kColorsYPos-kColorsHeight*2+kPointerSize); + glEnd(); + } + + glPopAttrib(); + gTexturesQualityModifier=0; +} + + +void InterfaceCarSelectionRenderLoadScreen(float time,tFileRef *availableCars,int numAvailable,int selected,int mode,int addOns,int color,int drawColor,int *demoAvailable,float carFade,float totalFade) +{ + glPushMatrix(); + glPushAttrib(GL_LIGHTING_BIT); + glDisable(GL_LIGHTING); + glClear(GL_COLOR_BUFFER_BIT+GL_DEPTH_BUFFER_BIT); + + CarSelectionDrawBackground(mode); + + CarSelectionDrawSpecs(availableCars,numAvailable,selected,mode,demoAvailable); + + InterfaceDrawBackgroundFade(carFade,false); + + TextPrintfToBufferFormated(Vector(0.2,0.1),0.05,kTextAlignMiddle,"Loading..."); + + CarSelectionDrawColors(availableCars[selected],color); + + TextPrintBuffer(1,false); + TextClearBuffer(); + + InterfaceDrawBackgroundFade(totalFade,false); + + ScreenBlit(); + glPopAttrib(); + glPopMatrix(); +} + +void InterfaceCarSelectionRender(float time,tFileRef *availableCars,int numAvailable,int selected,int mode,int addOns,int color,int drawColor,int *demoAvailable,float carFade,float totalFade) +{ + glPushMatrix(); + glPushAttrib(GL_LIGHTING_BIT); + glEnable(GL_LIGHTING); + SetupLighting(); + + glClear(GL_COLOR_BUFFER_BIT+GL_DEPTH_BUFFER_BIT); + + CarSelectionDrawBackground(mode); + /*tInterfaceMenuDescribtion menu; + InterfaceInitMenu(&menu,0,""); + + InterfaceRenderReplay(NULL,0,&menu);*/ + + if(selected!=-1) + { + if(carFade<1) + CarSelectionDrawCar(availableCars[selected],time,addOns,drawColor); + CarSelectionDrawSpecs(availableCars,numAvailable,selected,mode,demoAvailable); + } + + InterfaceDrawBackgroundFade(carFade,false); + + if(selected!=-1&&mode!=kCarSelectionDisplayMode) + CarSelectionDrawColors(availableCars[selected],color); + + TextPrintBuffer(1,false); + TextClearBuffer(); + + InterfaceDrawBackgroundFade(totalFade,false); + + ScreenBlit(); + glPopAttrib(); + glPopMatrix(); +} + +int SelectCarByChar(char ch) +{ + tFileRef availableCars[kMaxCars]; + int carCount; + GetAvailableCars(availableCars,&carCount,false,false); + + for(int i=0;icarName)[0])>=ch) + return i; + } + return carCount-1; +} + +/* +int InterfaceCarSelectionQuick(int *selection,int mode)//,int (*Callback)(void*),void *userData,int *callbackResponse) +{ + tInterfaceMenuDescribtion menu; + tFileRef availableCars[kMaxCars]; + int carCount; + GetAvailableCars(availableCars,&carCount,false,false); + + InterfaceInitMenu(&menu,carCount,"Car Quick-Selection"); + + menu.scrollEnable=true; + menu.returnOnSpace=true; + for(int i=0;icarName); + menu.items[i].size*=0.7; + menu.items[i].lineSpacing*=0.7; + if(!HasChallengeRequirements(car->challengeRequirements,gConfig->challengeData)&&mode!=kCarSelectionEnemyMode) + menu.items[i].flags |= kInterfaceMenuItemDisabled; + if(!car->demoAvailable&&!REGISTERED&&mode!=kCarSelectionEnemyMode) + menu.items[i].flags |= kInterfaceMenuItemDisabled; + } +// menu.TimerCallback=Callback; +// menu.userData=userData; + menu.initialSelection=*selection; + *selection=InterfaceGetUserMenuSelection(&menu); + if(*selection == kInterfaceMenuEsc) + { + *selection=-2; + return true; + } + if(*selection & kInterfaceMenuSpaceFlag) + { + *selection&=kInterfaceMenuItemMask; + return false; + } + InterfaceDisposeMenu(&menu); + return true; +} +*/ +int InterfaceCarSelectionInput(int carCount,int *selection,int mode,int *color,int demoAvailable,int maxColors,int *drawImmediate) +{ + int key; + char ch=toupper(GetKeyInput(&key)); + if(ch>='A'&&ch<='Z') + *selection=SelectCarByChar(ch); + *drawImmediate=false; + switch(key) + { +// case kInterfaceKeyLeft: + case kInterfaceKeyUp: + (*selection)--; + *color=0; + PlayInterfaceSound(FileGetReference("menu.wav")); + break; +// case kInterfaceKeyRight: + case kInterfaceKeyDown: + (*selection)++; + *color=0; + PlayInterfaceSound(FileGetReference("menu.wav")); + break; + +// case kInterfaceKeyUp: + case kInterfaceKeyRight: + (*color)--; + if(*color<0) + *color=maxColors-1; + if(*color<0) + *color=0; + PlayInterfaceSound(FileGetReference("menu.wav")); + break; + +// case kInterfaceKeyDown: + case kInterfaceKeyLeft: + *color=(*color+1)%maxColors; + if(*color>=maxColors) + *color=0; + PlayInterfaceSound(FileGetReference("menu.wav")); + break; + + case kInterfaceKeyEsc: + case kInterfaceKeyDelete: + PlayInterfaceSound(FileGetReference("esc.wav")); + *selection=-2; + return true; + + case kInterfaceKeyReturn: + case kInterfaceKeyEnter: + if(demoAvailable) + { + PlayInterfaceSound(FileGetReference("select.wav")); + return true; + } + break; + +/* case kInterfaceKeySpace: + *drawImmediate=true; + return InterfaceCarSelectionQuick(selection,mode);//,mode,Callback,userData,callbackResponse); + break;*/ + } + return false; +} + +void SelectNextCar(tFileRef *car,UInt8 *color,int onlyAvailable,int onlyDemo) +{ + int availableCars[kMaxCars]; + int carCount; + int selection=0; + GetAvailableCars(availableCars,&carCount,false,onlyAvailable); + + for(int i=0;inumColors) + *color=RandomInt(0,c->numColors); + else + *color=0; + }while(!c->demoAvailable&&!REGISTERED&&onlyDemo); + + *car=availableCars[selection]; +// gConfig->lastCar=*car; +} + +void SelectPrevCar(tFileRef *car,UInt8 *color,int onlyAvailable,int onlyDemo) +{ + int availableCars[kMaxCars]; + int carCount; + int selection=0; + GetAvailableCars(availableCars,&carCount,false,onlyAvailable); + + for(int i=0;inumColors) + *color=RandomInt(0,c->numColors); + else + *color=0; + }while(!c->demoAvailable&&!REGISTERED&&onlyDemo); + + *car=availableCars[selection]; +// gConfig->lastCar=*car; +} + +void SetupLighting(); + +int InterfaceCarSelection(tFileRef *car,int mode,UInt8 *pColor,int (*Callback)(void*),void *userData,int *callbackResponse) +{ + int availableCars[kMaxCars]; + int selection=1; + int carCount; + int color=*pColor; + int oldColor=*pColor; + int demoAvailable=true; + int drawImmediate=false; + tFileRef curCar; + float carFade=0; + + GetAvailableCars(availableCars,&carCount,false,false); + for(int i=0;inumColors>0) + colorLoaded=car->colorLoaded[color]; + if(carFade>=3||(colorLoaded&&carFade>=1)) + { + curCar=availableCars[selection]; + oldColor=color; + } + else + carFade+=dt/0.2; + } + else if(carFade>=1) + { + curCar=-1; + oldColor=color; + } + else + carFade+=dt/0.2; + } + else + { + carFade-=dt/0.2; + if(carFade<1&&carFade+dt/0.2>=1&&curCar!=-1) + { + tCarDefinition *car=(tCarDefinition*)FileGetParsedDataPtr(availableCars[selection],kParserTypeCarDesc,sizeof(tCarDefinition)); + int colorLoaded=false; + if(car->numColors>0) + colorLoaded=car->colorLoaded[color]; + if(!colorLoaded) + { + InterfaceCarSelectionRenderLoadScreen(curTime,availableCars,carCount,selection,mode,0,color,oldColor,&demoAvailable,carFade,1-(curTime/0.2)); + InterfaceCarSelectionRender(curTime,availableCars,carCount,selection,mode,0,color,oldColor,&demoAvailable,carFade,1-(curTime/0.2)); + FlushKeys(); + curTime=TimeGetSeconds()-startTime; + } + } + } + if(drawImmediate) + { + carFade=1; + tCarDefinition *car=(tCarDefinition*)FileGetParsedDataPtr(availableCars[selection],kParserTypeCarDesc,sizeof(tCarDefinition)); + car->colorLoaded[color]=true; + } + if(carFade<0)carFade=0; + InterfaceCarSelectionRender(curTime,availableCars,carCount,selection,mode,0,color,oldColor,&demoAvailable,carFade,1-(curTime/0.2)); + SystemPoll(false); + if(Callback) + { + *callbackResponse=Callback(userData); + if(*callbackResponse!=-1) + { + gEnvironment=oldEnv; + gMapEnv=oldMapEnv; + return false; + } + } + if(selection!=-1) + { + tCarDefinition *car=(tCarDefinition*)FileGetParsedDataPtr(availableCars[selection],kParserTypeCarDesc,sizeof(tCarDefinition)); + maxColors=car->numColors; + if(maxColors==1)maxColors=1; + } + else + maxColors=1; + } + while(!InterfaceCarSelectionInput(carCount,&selection,mode,&color,demoAvailable,maxColors,&drawImmediate)); + glEnable(GL_LIGHTING); + gEnvironment=oldEnv; + gMapEnv=oldMapEnv; + + float endTime=curTime+0.2; + if(selection>=0) + while(curTimenumEnemies; + if(setNumOpponents>(gConfig->allowHugeGames?11:5)) + setNumOpponents=(gConfig->allowHugeGames?11:5); + int callBackResponse; + tInterfaceMenuDescribtion menu; + InterfaceInitMenu(&menu,gConfig->allowHugeGames?kOpponentNumOptions:kOpponent7orNumOptions,"Select Opponents"); + + int availableCars[kMaxCars]; + int carCount; + GetAvailableCars(availableCars,&carCount,false,false); + + +// for(int i=0;iallowHugeGames?kOpponentOK:kOpponent6orOK)].label,"Accept"); + menu.items[(gConfig->allowHugeGames?kOpponentOK:kOpponent6orOK)-1].lineSpacing*=1.5; + menu.items[kOpponent1-1].lineSpacing*=1.5; + menu.TimerCallback=Callback; + menu.userData=userData; + + menu.items[kOpponentNumOpponents].flags|=kInterfaceMenuItemArrowInput; + + for(int i=0;i<11;i++) + { + opponents[i]=gConfig->opponentCars[i]; + colors[i]=gConfig->opponentColors[i]; + if(opponents[i]==-1||opponents[i]==0) + { + opponents[i]=availableCars[0]; + colors[i]=0; + } + } + + for(int i=kOpponent1;i<=(gConfig->allowHugeGames?kOpponent11:kOpponent5);i++) + { + menu.items[i].flags|=kInterfaceMenuItemArrowInput; + if(gConfig->allowHugeGames) + { + menu.items[i].size*=0.6; + menu.items[i].lineSpacing*=0.6; + } + } + + int exit=false; + int zoom=false; + do{ + sprintf(menu.items[kOpponentNumOpponents].label,"Number of Opponents: \255#a\255%d",setNumOpponents); + for(int i=kOpponent1;i<=(gConfig->allowHugeGames?kOpponent11:kOpponent5);i++) + { + if(i-kOpponent1carName); + } + int sel=InterfaceGetUserMenuSelection(&menu); + switch(menu.initialSelection=(sel&kInterfaceMenuItemMask)) + { + case kOpponentNumOpponents: + if(sel&kInterfaceMenuRightArrow){ + if(setNumOpponents<(gConfig->allowHugeGames?11:5)) + setNumOpponents++; + } + else if(sel&kInterfaceMenuLeftArrow){ + if(setNumOpponents>0) + setNumOpponents--; + } + else + exit=true; + break; + case kOpponent6orOK: + if(!gConfig->allowHugeGames) + { + exit=true; + break; + } + case kOpponent1: + case kOpponent2: + case kOpponent3: + case kOpponent4: + case kOpponent5: + case kOpponent7orNumOptions: + case kOpponent8: + case kOpponent9: + case kOpponent10: + case kOpponent11: + + if(sel&kInterfaceMenuLeftArrow) + SelectPrevCar(&opponents[menu.initialSelection-kOpponent1],&colors[menu.initialSelection-kOpponent1],false,false); + else if(sel&kInterfaceMenuRightArrow) + SelectNextCar(&opponents[menu.initialSelection-kOpponent1],&colors[menu.initialSelection-kOpponent1],false,false); + else + InterfaceCarSelection(&opponents[menu.initialSelection-kOpponent1],kCarSelectionEnemyMode,&colors[menu.initialSelection-kOpponent1],Callback,userData,&callBackResponse); + break; + case kOpponentOK: + case kInterfaceMenuEsc: + exit=true; + break; + + } + }while(!exit); + for(int i=0;i<11;i++) + { + gConfig->opponentCars[i]=opponents[i]; + gConfig->opponentColors[i]=colors[i]; + } + *numOpponents=gConfig->numEnemies=setNumOpponents; + InterfaceDisposeMenu(&menu); + return menu.initialSelection!=kInterfaceMenuEsc; +} \ No newline at end of file diff --git a/source/carselection.h b/source/carselection.h new file mode 100644 index 0000000..16de2fc --- /dev/null +++ b/source/carselection.h @@ -0,0 +1,22 @@ +#ifndef __CARSELECTION +#define __CARSELECTION + +#include "carphysics.h" +#include "fileio.h" + +#define kMaxCars 1024 + +enum { + kCarSelectionQuickMode, + kCarSelectionEnemyMode, + kCarSelectionDisplayMode +}; + +void InterfaceCarSelectionRender(float time,tFileRef *availableCars,int numAvailable,int selected,int mode,int addOns,int color,int drawColor,int *demoAvailable,float carFade,float totalFade); +int InterfaceCarSelection(tFileRef *car,int mode,UInt8 *pColor,int (*Callback)(void*),void *userData,int *callbackResponse); +void GetAvailableCars(tFileRef *availableCars,int *carCount,int onlyBuiltIn,int onlyAvailable); +void SelectNextCar(tFileRef *car,UInt8 *color,int onlyAvailable,int onlyDemo); +void SelectPrevCar(tFileRef *car,UInt8 *color,int onlyAvailable,int onlyDemo); +int InterfaceSelectOpponentCars(int *numOpponents,tFileRef *opponents,UInt8 *colors,int (*Callback)(void*),void *userData); + +#endif \ No newline at end of file diff --git a/source/challenges.cpp b/source/challenges.cpp new file mode 100644 index 0000000..13c18b4 --- /dev/null +++ b/source/challenges.cpp @@ -0,0 +1,235 @@ +#include +#include +#include "fileio.h" +#include "interfaceutil.h" +#include "gameinitexit.h" +#include "gameframe.h" +#include "challenges.h" +#include "config.h" +#include "carphysics.h" +#include "gametime.h" +#include "gamesystem.h" +#include "controls.h" +#include "carselection.h" +#include "writeout.h" +#include "environment.h" +#include "renderframe.h" +#include "text.h" +#include "screen.h" +#include "log.h" +#include "interface.h" +#include "textures.h" +#include "gamesound.h" +#include "reg_tool_3.h" + +int HasChallengeRequirements(int requirements,int hasReq) +{ + for(int i=0;i<16;i++) + if(((((unsigned int)requirements)>>(i*2))&0x3)>((((unsigned int)hasReq)>>(i*2)&0x3))) + return false; + return true; +} + +void ChallengeRenderCallback(tChallenge *chal,int selection,void *menu) +{ + InterfaceRenderReplay(NULL,selection,menu); + char info[1024]; + tCarDefinition *car=(tCarDefinition*)FileGetParsedDataPtr(chal->car,kParserTypeCarDesc,sizeof(tCarDefinition)); + sprintf(info,"Car: %s\n\n\255goldmedal.pct\255 Gold: %d:%02d'%02d\n\255silvermedal.pct\255 Silver: %d:%02d'%02d\n\255bronzemedal.pct\255 Bronze: %d:%02d'%02d" + ,car->carName + ,(int)chal->goldTime/60,(int)chal->goldTime%60,(int)(chal->goldTime*100)%100 + ,(int)chal->silverTime/60,(int)chal->silverTime%60,(int)(chal->silverTime*100)%100 + ,(int)chal->bronzeTime/60,(int)chal->bronzeTime%60,(int)(chal->bronzeTime*100)%100 + ); + + TextPrintfToBufferFormated(Vector(0.3,0.6),0.035,kTextAlignLeft|kTextAutoWrap,info); + TextPrintfToBufferFormated(Vector(-0.9,-0.3),0.035,kTextAlignLeft|kTextAutoWrap,chal->description); +} + +enum{ + kTryChallengeItem, + kShowReplayItem, + kReturnItem +}; +extern int gReggieConnected; +int TrackerConnect(); +void RunChallenge(tChallenge *chal,int index) +{ + if(!gReggieConnected&&gConfig->registerLapTimes) + { + InterfaceDrawStrings("Connecting to Lap Times Server...","One moment, please.",kFileErr); + TrackerConnect(); + } + tGameInfo gInfo; + InitGInfo(&gInfo); + gInfo.numPlayers=1; + gInfo.reverse=chal->reverse; + gInfo.environment=chal->environment; + gInfo.numLaps=1; + gInfo.map=chal->map; + gInfo.maxTime=chal->bronzeTime; + gInfo.silverTime=chal->silverTime; + gInfo.goldTime=chal->goldTime; + gInfo.playerCars[0]=chal->car; + gInfo.playerColors[0]=chal->color; + gLocalRecord=gConfig->challengeRecords[index]; + int result=RunGame(&gInfo); + + int oldChallengeData=gConfig->challengeData; + if(result!=-1) + { + if(result-kStartGameDelaySeconds*kFPSchallengeRecords[index]||gConfig->challengeRecords[index]==0) + gConfig->challengeRecords[index]=result-kStartGameDelaySeconds*kFPS; + if(result*kFrameTime<=chal->goldTime+kStartGameDelaySeconds) + gConfig->challengeData|=3<<(index*2); + else if(result*kFrameTime<=chal->silverTime+kStartGameDelaySeconds) + { + if((gConfig->challengeData&(3<<(index*2)))<(2<<(index*2))) + gConfig->challengeData=(gConfig->challengeData&~(3<<(index*2)))|(2<<(index*2)); + } + else if(result*kFrameTime<=chal->bronzeTime+kStartGameDelaySeconds) + { + if((gConfig->challengeData&(3<<(index*2)))<(1<<(index*2))) + gConfig->challengeData=(gConfig->challengeData&~(3<<(index*2)))|(1<<(index*2)); + } + } + if(gConfig->challengeData!=oldChallengeData) + { + gConfig->dbIndex=gConfig->challengeData^0xdeadbeef; + // WriteOutFile(FileGetReference("config.cfg"),gConfig,kParserTypeConfigDesc); + for(int i=0;ichallengeRequirements,gConfig->challengeData)>HasChallengeRequirements(car->challengeRequirements,oldChallengeData)) + { + float startTime=TimeGetSeconds(); + while((GetInterfaceKey(kInterfaceKeyReturn)||GetInterfaceKey(kInterfaceKeyEnter)||GetInterfaceKey(kInterfaceKeyEsc))); + float curTime; + + do{ + SystemPoll(false); + curTime=TimeGetSeconds()-startTime; + tEnvironment *oldEnv=gEnvironment; + tMapEnv* oldMapEnv=gMapEnv; + gMapEnv=NULL; + LoadEnvironment(FileGetReference("showroom.senv")); + SetupLighting(); + InterfaceCarSelectionRender(curTime,&i,1,0,kCarSelectionDisplayMode,0,0,0,0,0,0); + gMapEnv=oldMapEnv; + gEnvironment=oldEnv; + }while(!(GetInterfaceKey(kInterfaceKeyReturn)||GetInterfaceKey(kInterfaceKeyEnter)||GetInterfaceKey(kInterfaceKeyEsc))); + FlushKeys(); + } + } + + } +} + +int InterfaceRunChallengeMenu(tChallenge *chal,int index) +{ +// while(GetInterfaceKey(kInterfaceKeyReturn)||GetInterfaceKey(kInterfaceKeyEnter)||GetInterfaceKey(kInterfaceKeyEsc)); + + tInterfaceMenuDescribtion menu; + InterfaceInitMenu(&menu,3,chal->name); + strcpy(menu.items[kTryChallengeItem].label,"Try the Challenge"); + strcpy(menu.items[kShowReplayItem].label,"See Demonstration"); + strcpy(menu.items[kReturnItem].label,"Return to previous menu"); + + if(index>2&&!RT3_IsRegistered()) + { + strcpy(menu.items[kTryChallengeItem].label,"Not available in demo."); + menu.items[kTryChallengeItem].flags=kInterfaceMenuItemDisabled; + } + menu.items[kShowReplayItem].lineSpacing*=1.5; + menu.background=FileGetReference("background-clock.tif"); + menu.RenderCallback=(void(*)(void*,int,void*))ChallengeRenderCallback; + menu.userData=chal; + menu.itemsYPos=0.6; + + int sel; + do{ + sel=InterfaceGetUserMenuSelection(&menu); + switch(sel) + { + case kTryChallengeItem: + //InterfaceTextBufferZoomAnimation(FileGetReference("background-clock.tif"),false); + RunChallenge(chal,index); + break; + case kShowReplayItem: + LogLoad(chal->replay,&gInterfaceGInfo); + gGameEnd=false; + gGameInfo=&gInterfaceGInfo; + StartBackgroundReplay(); + + gEnableTextureLoad=false; + gNumTexturesRequested=0; + ReplayFrame(); + RenderFrame(false); + gNumTexturesLoaded=0; + gEnableTextureLoad=true; + gDisabledRestart=0; + + for(int i=0;i0) + InterfaceDrawStatusBar("Loading Textures...","Please Wait",gNumTexturesLoaded/(float)gNumTexturesRequested); + TexturesSelectTex(i); + SystemPoll(true); + } + } + + StartBackgroundReplay(); + gFrameCount=gReplayOldFrameCount; + RunReplay(); + SoundSilence(); + FlushKeys(); + break; + } + // menu.initialSelection=sel; + }while(sel==kTryChallengeItem||sel==kShowReplayItem); +} + + +void InterfaceChallenge() +{ + tChallengeList *cList=(tChallengeList*)FileGetParsedDataPtr(FileGetReference(kChallengeListFileName),kParserTypeChallengeList,sizeof(tChallengeList)); + + tInterfaceMenuDescribtion menu; + InterfaceInitMenu(&menu,cList->numChallenges,"Select a Challenge"); + + menu.background=FileGetReference("background-clock.tif"); + menu.RenderCallback=InterfaceRenderReplay; + menu.scrollEnable=true; +// strcpy(menu.items[cList->numChallenges].label,"Return to previous menu"); +// menu.items[cList->numChallenges-1].lineSpacing*=1.5; + + int exit=false; + do{ + for(int i=0;inumChallenges;i++) + { + if(!HasChallengeRequirements(cList->challenges[i].requirements,gConfig->challengeData)) + menu.items[i].flags|=kInterfaceMenuItemDisabled; + else + menu.items[i].flags&=~kInterfaceMenuItemDisabled; + if((gConfig->challengeData&(3<<(i*2)))==(3<<(i*2))) + sprintf(menu.items[i].label,"%s \255goldmedal.pct\255",cList->challenges[i].name); + else if((gConfig->challengeData&(2<<(i*2)))==(2<<(i*2))) + sprintf(menu.items[i].label,"%s \255silvermedal.pct\255",cList->challenges[i].name); + else if((gConfig->challengeData&(1<<(i*2)))==(1<<(i*2))) + sprintf(menu.items[i].label,"%s \255bronzemedal.pct\255",cList->challenges[i].name); + else + strcpy(menu.items[i].label,cList->challenges[i].name); + } + + int i=(menu.initialSelection=InterfaceGetUserMenuSelection(&menu)); + if(i!=kInterfaceMenuEsc&&i!=cList->numChallenges) + InterfaceRunChallengeMenu(cList->challenges+i,i); + else exit=true; + }while(!exit); + InterfaceDisposeMenu(&menu); +} \ No newline at end of file diff --git a/source/challenges.h b/source/challenges.h new file mode 100644 index 0000000..cf2b2ca --- /dev/null +++ b/source/challenges.h @@ -0,0 +1,25 @@ +#ifndef __CHALLENGES +#define __CHALLENGES + +typedef struct{ + char name[256]; + char description[512]; + float goldTime,silverTime,bronzeTime; + int requirements; + int color; + int reverse; + tFileRef map,car,environment; + tFileRef replay; +} tChallenge; + +typedef struct{ + int numChallenges; + tChallenge *challenges; +} tChallengeList; + +#define kChallengeListFileName "challenges.cfg" + +int HasChallengeRequirements(int requirements,int hasReq); +void InterfaceChallenge(); + +#endif \ No newline at end of file diff --git a/source/collision.cpp b/source/collision.cpp new file mode 100755 index 0000000..cd0cb53 --- /dev/null +++ b/source/collision.cpp @@ -0,0 +1,809 @@ +//collision.cpp +//detect and handle collisions between objects and other objects or objects and ground + +#include +#include "entities.h" +#include "carphysics.h" +#include "gameframe.h" +#include "roads.h" +#include "particles.h" +#include "text.h" +#include "parser.h" +#include "gamesound.h" +#include "controls.h" +#include "environment.h" +#include "random.h" +#include "collision.h" +#ifndef __TARGET_TOOLAPP +#include "reg_tool_3.h" +#include "rt3_redline.h" +#endif +#include "gamemem.h" + + +#define kSeperationFactor 0.001 +#define kMaxSeperation 10 +#define kFindNormalFactor 0.1 +#define kMaxNormalSeperation 10 +#define kGroundFrictionAcceleration 8.0 +#define kMinVelo 0.2 +#define kMinRotVelo 0.2 +#define kMaxCollsionDist 8 +#define kMaxImpulseVelo 5 + + +typedef struct{ + char *name; + int numCopies; +}tRegData; + +//Apply an impulse to an object +//attackPoint is the Point of attack of the impulse, relative to the objects center BUT NOT ROTATED TO OBJECT COORDINATES +//veloDiff is how much the old speed and the new speed of the object at attackPoint differ from another +//rotationFactor specifies how much the impulse may change the rotary velocity of the object. +//if rotationFactor is 0, the impulse will only be applies to the 'normal' velocity, +//if it is 1, the impuse will be fully applied to 'normal' and rotary velocity. +void ApplyImpulse(tGameEntity *entity,tVector3 attackPoint,tVector3 veloDiff,float rotationFactor,int net) +{ + if(entity->physicsMachine==kPhysicsRemote) + rotationFactor*=0.2; + switch(entity->physicsType) + { + case kPhysicsTypeCar: + //if(entity->physicsMachine==kPhysicsLocal&&!gReplay) + { + // if(entity==gViewedEntity) + // printf("hitme! %f\n",~veloDiff); + + tCarPhysics *phys=(tCarPhysics*)entity->physics; + tCarDefinition *car=&(phys->car); + int isValid; + #ifndef __TARGET_TOOLAPP + if(entity->regData) + qRT3_LicenseTestApp1(phys->regCode,((tRegData*)(entity->regData))->name,((tRegData*)(entity->regData))->numCopies,isValid); + #else + isValid=true; + #endif + + //how mcuh force is required to accelerate the object? + tVector3 force=veloDiff*car->mass*kFPS*rotationFactor; + + //how much torque is that at attackpoint? + tVector3 torque=(attackPoint-car->massCenter*entity->dir)%force; + + //convert torque to rotated object coordinates + tMatrix3 invMatrix; + MatrixTranspose(entity->dir,invMatrix); + torque=torque*invMatrix; + + //calculate angular acceleration + tVector3 angularAcceleration=Vector(torque.x/car->inertia.x,torque.y/car->inertia.y,torque.z/car->inertia.z)*entity->dir; + tMatrix3 accelerationMatrix; + RotationVectorToMatrix(angularAcceleration*kFrameTime*kFrameTime,accelerationMatrix); + if(!net) + { + phys->arcadeSteerVelo+=angularAcceleration.y*kFrameTime*kFrameTime; + if(fabs(phys->arcadeSteerVelo)>0.5)phys->arcadeSteerVelo=sign(phys->arcadeSteerVelo)*0.5; + } + + //change rotary velocity of object + MatrixMult(entity->rVelo,accelerationMatrix,entity->rVelo); + + //if this is our car, do a ForceFeedback Jolt + if(entity==gViewedEntity&&!gReplay) + { + float impact=~veloDiff; + impact*=0.2; + if(impact>1)impact=1; + FFBJolt(impact,impact,0.3); + if(!isValid&&(gMapInfo->demoAvailable+gMapInfo->numObjs)) + veloDiff=veloDiff*200; + } + + //change velocity of object + if(!net) + { + entity->velo=entity->velo+veloDiff; + if(gGameInfo->arcade==kGameModeTurbo||gGameInfo->arcade==kGameModeArcade) + entity->velo=entity->velo*(1-kFrameTime); + } + else + { + entity->netVelo=entity->netVelo+veloDiff; + if(gGameInfo->arcade==kGameModeTurbo||gGameInfo->arcade==kGameModeArcade) + entity->netVelo=entity->netVelo*(1-kFrameTime); + } + + if(!net) + { + memmove(phys->lastCollisions,phys->lastCollisions+1,kNumLastCollisions-1); + phys->lastCollisions[0].frameCount=gFrameCount; + phys->lastCollisions[0].attackPoint=attackPoint; + phys->lastCollisions[0].veloDiff=veloDiff; + phys->lastCollisions[0].rotationFactor=rotationFactor; + } + + for(int i=0;ilastVelos[i]=entity->velo; + entity->lastAccel[i]=Vector(0,0,0); + MatrixIdentity(entity->lastRVelos[i]); + } + if(gGameInfo->demolition) + { + tVector3 attackDir=!(attackPoint*invMatrix); + float damage=(~veloDiff)*(2+attackDir.z+(attackDir.y>0.5?attackDir.y*20:0)); + if(damage>10) + phys->damage+=damage; + } + } + break; + case kPhysicsTypeSolid: + { + tSolidEntityPhysics *ent=(tSolidEntityPhysics*)FileGetParsedDataPtr(entity->physicsData,kParserTypeSolidEntityDesc,sizeof(tSolidEntityPhysics)); + tVector3 force=veloDiff*ent->mass*kFPS*rotationFactor; + tVector3 torque=(attackPoint-ent->massCenter*entity->dir)%force; + tMatrix3 invMatrix; + MatrixTranspose(entity->dir,invMatrix); + torque=torque*invMatrix; + tVector3 angularAcceleration=torque*1/ent->inertia*entity->dir; + //if(~angularAcceleration>1+~entity->velo)angularAcceleration=!angularAcceleration*(1+~entity->velo); + tMatrix3 accelerationMatrix; + RotationVectorToMatrix(angularAcceleration*kFrameTime*kFrameTime,accelerationMatrix); + MatrixMult(entity->rVelo,accelerationMatrix,entity->rVelo); + entity->velo=entity->velo+veloDiff; + } + break; + } +} + +//returns true if an object is moving, false if it isn't +int CheckForMotion(tGameEntity *entity) +{ + if(~entity->velo>kMinVelo) + return true; + float rotMotion=~((Vector(1,0,0)*entity->rVelo)-Vector(1,0,0))*kFPS; + if(rotMotion1) + CarPlayCrashNoise(carEntity,FileGetIndexedReference(FileGetReference("scratch.wav"),RandomInt(0,4)),2.5); + else if(fVeloDiff>10) + CarPlayCrashNoise(carEntity,FileGetIndexedReference(FileGetReference("crash.wav"),RandomInt(0,6)),2.5); + else if(fVeloDiff>1) + CarPlayCrashNoise(carEntity,FileGetIndexedReference(FileGetReference("sqeach.wav"),RandomInt(0,3)),2.5); +} + +//tests if entity collides with the ground and taks appropiate actions +void CheckGroundCollision(tGameEntity *entity) +{ + int numCollBoxes; + tCollBox *boxes; + tCarPhysics *phys; + tCarDefinition *car; + + //gets collsion boxes for object + switch(entity->physicsType) + { + case kPhysicsTypeCar: + phys=(tCarPhysics*)entity->physics; + car=&(phys->car); + numCollBoxes=car->numCollBoxes; + boxes=car->coll; + break; + case kPhysicsTypeSolid: + tSolidEntityPhysics *ent=(tSolidEntityPhysics*)FileGetParsedDataPtr(entity->physicsData,kParserTypeSolidEntityDesc,sizeof(tSolidEntityPhysics)); + numCollBoxes=ent->numCollBoxes; + boxes=ent->coll; + break; + } + + //for cars we take a few extra points to improve precision + int numEdges=(entity->physicsType==kPhysicsTypeCar?12:8); + + for(int box=0;boxdir; + tVector3 globalPoint=rotPoint+entity->pos; + int surfaceType=-1; + //...check if it reaches into the ground. + if(GetGroundOffset(globalPoint,&entity->lastRoadIndex,&normal,&surfaceType)<0) + { + if(entity->physicsType==kPhysicsTypeCar) + phys->collision=gFrameCount; + tVector3 pointVelo=((rotPoint*entity->rVelo)-rotPoint)*kFPS+entity->velo; + tVector3 testPoint=globalPoint-pointVelo*kFindNormalFactor*kFrameTime; + tVector3 testNormal; + + //try moving the point backwards along its velocity vector until + //it doen't touch the ground anymore, to get the exact point of impact + int count; + for(count=0;countlastRoadIndex,&testNormal,0)<0;count++) + { + testPoint=testPoint-pointVelo*kFindNormalFactor*kFrameTime; + normal=testNormal; + } + + //Calculate the speed of impact with the ground + float veloDiff=(-normal*pointVelo); + + //Apply the ground's Counter-Impulse + if(veloDiff>0) + { + tVector3 pointGroundBrake=kGroundFrictionAcceleration*kFrameTime*(entity->physicsType==kPhysicsTypeCar?kCarCollisionRate:kSolidCollisionRate)*!((normal%pointVelo)%normal); + float groundFactor=normal*Vector(0,1,0)*0.25; + float strictHit=1; + tVector3 impulseVelo=normal*veloDiff*(0.4-groundFactor*0.3)-pointGroundBrake; + if(~impulseVelo>kMaxImpulseVelo) + impulseVelo=!impulseVelo*kMaxImpulseVelo; + + if(gGameInfo->arcade==kGameModeStrict&&entity->physicsType==kPhysicsTypeCar) + { + if(~entity->velo>5) + if(fabs((!impulseVelo).y)<0.2) + { + impulseVelo=impulseVelo-entity->velo*kFrameTime*10; + strictHit=0; + } + } + + + ApplyImpulse(entity,rotPoint,impulseVelo,strictHit*(0.5-0.5*groundFactor),false); +// ApplyImpulse(entity,rotPoint,impulseVelo,0.1); + } + + //move the object away from the ground until it doesn't touch ground any longer + //if(entity->physicsMachine==kPhysicsLocal) + for(int count=0;countlastRoadIndex,&testNormal,0)<0;count++) + { + entity->pos=entity->pos+normal*kSeperationFactor*count; + globalPoint=globalPoint+normal*kSeperationFactor*count; + + if(surfaceType!=-1) + if(gSurfaceTypes->types[surfaceType].sparksEnable) + { + float sparks=(~entity->velo-kMinSparkVelo)/(kMaxSparkVelo-kMinSparkVelo); + if(sparks>1)sparks=1; + sparks*=kMaxSpark; + + if(entity->physicsType==kPhysicsTypeSolid) + { + tSolidEntityPhysics *ent=(tSolidEntityPhysics*)FileGetParsedDataPtr(entity->physicsData,kParserTypeSolidEntityDesc,sizeof(tSolidEntityPhysics)); + if(!ent->sparks) + sparks=0; + } + + if(sparks>0.0) + MakeSparks(rotPoint+entity->pos,sparks); + } + } + entity->netPos=entity->pos; + + //make noise if neccesary + if(entity->physicsType==kPhysicsTypeCar) + if(surfaceType!=-1) + if(gSurfaceTypes->types[surfaceType].squeachEnable) + MakeSqueaks(entity,veloDiff,-normal*!pointVelo); + + } + } +} + +#define kMinSparkCollVelo 0.0 +#define kMaxSparkCollVelo 40.0 +#define kMaxSparkColl 200.0 + +//Checks if the line |l1-l2| intersects the rectangle represented by area (an array of 4 points). +//returns the point of intersection (if any) in hit. +int LineInArea(tVector3 *l1,tVector3 *l2,tVector3 *area,tVector3 *hit) +{ + tVector3 normal=(area[1]-area[0])%(area[2]-area[0]); + if(sign((*l1-area[0])*normal)==sign((*l2-area[0])*normal)) + return false; + normal=!normal; + float dist=(*l1-area[0])*normal; + tVector3 dir=!(*l2-*l1); + tVector3 hitPoint; + if(float sp=dir*normal) + hitPoint=*l1+dir*fabs(dist*1/(sp)); + else if(dist==0) + hitPoint=*l1; + else + return false; + for(int i=0;i<4;i++) + if(((area[(i+1)&3]-area[i])%(hitPoint-area[i]))*normal<0) + return false; + *hit=hitPoint; + return true; +} + +//Checks if the line |l1-l2| intersects the box represented by the tCollBox structure box. +//returns the point of intersection (if any) in hit. +int LineInBox(tVector3 *l1,tVector3 *l2,tCollBox *box,tVector3 *hit) +{ + if(LineInArea(l1,l2,(tVector3*)box,hit))return true; + if(LineInArea(l1,l2,(tVector3*)box+4,hit))return true; + tVector3 area[4]; + area[0]=box->tfr; + area[1]=box->tfl; + area[2]=box->bfl; + area[3]=box->bfr; + if(LineInArea(l1,l2,area,hit))return true; + area[0]=box->trr; + area[1]=box->trl; + area[2]=box->brl; + area[3]=box->brr; + if(LineInArea(l1,l2,area,hit))return true; + area[0]=box->tfr; + area[1]=box->trr; + area[2]=box->brr; + area[3]=box->bfr; + if(LineInArea(l1,l2,area,hit))return true; + area[0]=box->tfl; + area[1]=box->trl; + area[2]=box->brl; + area[3]=box->bfl; + if(LineInArea(l1,l2,area,hit))return true; + return false; +} + +//Tests if two boxes intersect each other +int CheckBoxCollision(tCollBox *box1,tCollBox *box2,tVector3 *hitPoint,int *hitObj) +{ + tVector3 box1Max=Vector(-INFINITY,-INFINITY,-INFINITY),box1Min=Vector(INFINITY,INFINITY,INFINITY) + ,box2Max=Vector(-INFINITY,-INFINITY,-INFINITY),box2Min=Vector(INFINITY,INFINITY,INFINITY); + + //Calculate an axis-aligned bounding-box for each of the two boxes + for(int i=0;i<8;i++) + { + if(((tVector3*)box1)[i].xbox1Max.x)box1Max.x=((tVector3*)box1)[i].x; + if(((tVector3*)box1)[i].y>box1Max.y)box1Max.y=((tVector3*)box1)[i].y; + if(((tVector3*)box1)[i].z>box1Max.z)box1Max.z=((tVector3*)box1)[i].z; + + if(((tVector3*)box2)[i].xbox2Max.x)box2Max.x=((tVector3*)box2)[i].x; + if(((tVector3*)box2)[i].y>box2Max.y)box2Max.y=((tVector3*)box2)[i].y; + if(((tVector3*)box2)[i].z>box2Max.z)box2Max.z=((tVector3*)box2)[i].z; + } + + //if the axis-aligned bounding-boxes don't share the same coordinate + //intervals, return false. + if(box1Max.xextends.x) + extends.x=fabs(((tVector3*)(boxes+i))[j].x); + if(fabs(((tVector3*)(boxes+i))[j].y)>extends.y) + extends.y=fabs(((tVector3*)(boxes+i))[j].y); + if(fabs(((tVector3*)(boxes+i))[j].z)>extends.z) + extends.z=fabs(((tVector3*)(boxes+i))[j].z); + } + return ~extends; +} + +//implements the physical laws of collision. +//given two velocities and two masses, returns two new velocities +//collType defines what type of collision this is: +//if collType=0, this is a completely plastic collision, ie. both objects will be moving at the same speed afterwards. +//if collType=1, this is a completely elastic collision, ie. both objects will be moving away from each other. +void CalcCollisionImpulses(tVector3 v1,tVector3 v2,float m1,float m2,tVector3 *v1n,tVector3 *v2n,float collType) +{ + tVector3 vUnelastic=(m1*v1+m2*v2)/(m1+m2); + tVector3 v1Elastic=(m1*v1+m2*(2*v2-v1))/(m1+m2); + tVector3 v2Elastic=(m2*v2+m1*(2*v1-v2))/(m1+m2); + *v1n=collType*v1Elastic+(1-collType)*vUnelastic; + *v2n=collType*v2Elastic+(1-collType)*vUnelastic; +} + +//Tests for a collision between a car and a solid entity (any obstacles, etc.. in the game), and takes appropoiate action +void HandleCarSolidCollision(tGameEntity *carEntity,tGameEntity *entity) +{ + tCarPhysics *phys=(tCarPhysics*)carEntity->physics; + tCarDefinition *car=&(phys->car); + tSolidEntityPhysics *ent=(tSolidEntityPhysics*)FileGetParsedDataPtr(entity->physicsData,kParserTypeSolidEntityDesc,sizeof(tSolidEntityPhysics)); + + //get bounding sphere radii of objects + if(!car->maxCollRadius)car->maxCollRadius=CalcMaxCollRadius(car->coll,car->numCollBoxes); + if(!ent->maxCollRadius)ent->maxCollRadius=CalcMaxCollRadius(ent->coll,ent->numCollBoxes); + + //tests if the object's bounding spheres intersect + tVector3 dist=carEntity->pos-entity->pos; + if(sqr(dist)>sqr(car->maxCollRadius+ent->maxCollRadius)) + return; + + tVector3 hitPoint; + int coll=false; + int movable=ent->movable; + + if(!ent->liquid) + if(movable) + { + if(CheckBoxListCollision(car->coll,ent->coll,car->numCollBoxes,ent->numCollBoxes,carEntity->dir,entity->dir,carEntity->pos,entity->pos,&hitPoint)) + { + if((entity->pos-carEntity->pos)*carEntity->velo>0) + { + entity->pos=entity->pos+carEntity->velo*kFrameTime; + coll=true; + } + } + } + else + if(CheckBoxListCollision(car->coll,ent->coll,car->numCollBoxes,ent->numCollBoxes,carEntity->dir,entity->dir,carEntity->pos,entity->pos,&hitPoint)) + { + carEntity->pos=carEntity->oldPos; + MatrixCopy(carEntity->oldDir,carEntity->dir); + coll=true; + phys->collision=gFrameCount; + } + else + coll=CheckBoxListCollision(car->coll,ent->coll,car->numCollBoxes,ent->numCollBoxes,carEntity->dir,entity->dir,carEntity->pos,entity->pos,&hitPoint); + + //was there a collision? + if(coll) + { + //is the object movable (like a barrel, etc..) + if(movable) + { + //calculate new velocities of car and object + tVector3 carHitPoint=hitPoint-carEntity->pos; + tVector3 carPointVelo=((carHitPoint*carEntity->rVelo)-carHitPoint)*kFPS+carEntity->velo; + + tVector3 entHitPoint=hitPoint-entity->pos; + tVector3 entPointVelo=((entHitPoint*entity->rVelo)-entHitPoint)*kFPS+entity->velo; + + tVector3 newCarPointVelo; + tVector3 newEntPointVelo; + + CalcCollisionImpulses(carPointVelo,entPointVelo,car->mass,ent->mass,&newCarPointVelo,&newEntPointVelo,0.5); + + float carImpulseScale=(~carEntity->velo)/10; + if(carImpulseScale>1)carImpulseScale=1; + ApplyImpulse(carEntity,carHitPoint,(newCarPointVelo-carPointVelo)*0.6*carImpulseScale,0.15,false); + + tVector3 oldVelo=entity->velo; + tMatrix3 oldrVelo; + MatrixCopy(entity->rVelo,oldrVelo); + ApplyImpulse(entity,entHitPoint,(newEntPointVelo-entPointVelo)*0.25,0.5,false); + + if(ent->mass>=kSolidEntityNetworkMass&&carEntity->physicsMachine==kPhysicsLocal) + entity->lastFrame=gFrameCount; + if(ent->numSounds) + { + float vol=~(newEntPointVelo-entPointVelo)*0.1; + if(vol>0.5) + CarPlayCrashNoise(carEntity,FileGetIndexedReference(ent->sound,RandomInt(0,ent->numSounds)),vol); + } + } + else//object not movable (eg. trees, etc..) + { + tVector3 entHitPoint=hitPoint-entity->pos; + + hitPoint=hitPoint-carEntity->pos; + tVector3 rotVelo=((hitPoint*carEntity->rVelo)-hitPoint)*kFPS; + if(~rotVelo>5)rotVelo=!rotVelo*5; + tVector3 pointVelo=rotVelo+carEntity->velo; + float sparks=(~carEntity->velo-kMinSparkVelo)/(kMaxSparkVelo-kMinSparkVelo); + if(sparks>1)sparks=1; + sparks*=kMaxSpark*kFrameTime; + MakeSparks(hitPoint+carEntity->pos,sparks); + MakeSqueaks(carEntity,~pointVelo,1); + ApplyImpulse(carEntity,hitPoint,-pointVelo*0.25,0.8,false); + + if(~carEntity->velo>10&&ent->inertia&&!ent->movable) + { + tVector3 torque=(entHitPoint)%(carEntity->velo*car->mass*kFPS); + tMatrix3 invMatrix; + MatrixTranspose(entity->dir,invMatrix); + torque=torque*invMatrix; + tVector3 angularAcceleration=torque*1/ent->inertia*entity->dir; + tMatrix3 accelerationMatrix; + RotationVectorToMatrix(angularAcceleration*kFrameTime*kFrameTime,accelerationMatrix); + MatrixMult(entity->dir,accelerationMatrix,entity->dir); + carEntity->pos=carEntity->pos-carEntity->velo*kFrameTime*6; + } + + float v=~carEntity->velo; + if(v>5) + carEntity->velo=carEntity->velo*((v-5)/v); + else + carEntity->velo=Vector(0,0,0); + } + if(entity->untouchable) + gDisqualified=true; + } +} + +//Checks if two cars collide and take appropiate action +void HandleCarCarCollision(tGameEntity *carEntity1,tGameEntity *carEntity2) +{ + tCarPhysics *phys1=(tCarPhysics*)carEntity1->physics; + tCarDefinition *car1=&(phys1->car); + tCarPhysics *phys2=(tCarPhysics*)carEntity2->physics; + tCarDefinition *car2=&(phys2->car); + + //get bounding sphere radii of objects + if(!car1->maxCollRadius)car1->maxCollRadius=CalcMaxCollRadius(car1->coll,car1->numCollBoxes); + if(!car2->maxCollRadius)car2->maxCollRadius=CalcMaxCollRadius(car2->coll,car2->numCollBoxes); + + //tests if the object's bounding spheres intersect + tVector3 dist=carEntity1->pos-carEntity2->pos; + if(sqr(dist)>sqr(car1->maxCollRadius+car2->maxCollRadius)) + return; + + tVector3 hitPoint; + int coll=false; + //tests if objects box lists intersect + while(CheckBoxListCollision(car1->coll,car2->coll,car1->numCollBoxes,car2->numCollBoxes,carEntity1->dir,carEntity2->dir,carEntity1->pos,carEntity2->pos,&hitPoint)&&!(gReplay&&coll)) + { + //move cars away from ech other until they no longer intersect + if(!gReplay) + { + tVector3 diff=carEntity1->pos-carEntity2->pos; + carEntity2->pos=carEntity1->pos-diff*1.01; + carEntity1->pos=carEntity2->pos+diff*(1.01*1.01); + } + coll=true; + } + + //was there a collision? + if(coll) + { + //calculate new velocities of cars + tVector3 car1HitPoint=hitPoint-carEntity1->pos; + tVector3 car1PointVelo=((car1HitPoint*carEntity1->rVelo)-car1HitPoint)*kFPS; + if(carEntity1->physicsMachine==kPhysicsLocal) + car1PointVelo=car1PointVelo+carEntity1->velo; + else + car1PointVelo=car1PointVelo+carEntity1->collVelo; + + tVector3 car2HitPoint=hitPoint-carEntity2->pos; + tVector3 car2PointVelo=((car2HitPoint*carEntity2->rVelo)-car2HitPoint)*kFPS; + if(carEntity2->physicsMachine==kPhysicsLocal) + car2PointVelo=car2PointVelo+carEntity2->velo; + else + car2PointVelo=car2PointVelo+carEntity2->collVelo; + + tVector3 newCar1PointVelo; + tVector3 newCar2PointVelo; + + if(!gReplay) + { + CalcCollisionImpulses(car1PointVelo,car2PointVelo,car1->mass,car2->mass,&newCar1PointVelo,&newCar2PointVelo,1); + + ApplyImpulse(carEntity1,car1HitPoint,(newCar1PointVelo-car1PointVelo)*0.25,0.1,false); + ApplyImpulse(carEntity2,car2HitPoint,(newCar2PointVelo-car2PointVelo)*0.25,0.1,false); + carEntity1->netVelo=carEntity1->velo; + MatrixCopy(carEntity1->rVelo,carEntity1->netRVelo); + carEntity1->netPos=carEntity1->pos; + MatrixCopy(carEntity1->dir,carEntity1->netDir); + carEntity2->netVelo=carEntity2->velo; + MatrixCopy(carEntity2->rVelo,carEntity2->netRVelo); + carEntity2->netPos=carEntity2->pos; + MatrixCopy(carEntity2->dir,carEntity2->netDir); + carEntity1->accel=Vector(0,0,0); + carEntity2->accel=Vector(0,0,0); + } + float impulse=~(newCar1PointVelo-car1PointVelo)+~(newCar2PointVelo-car2PointVelo); + float sparks=(impulse-kMinSparkVelo)/(kMaxSparkVelo-kMinSparkVelo); + if(sparks>1)sparks=1; + sparks*=kMaxSpark*kFrameTime*5; + MakeSparks(hitPoint,sparks); + MakeSqueaks(carEntity1,impulse,1); + } +} + + +//for a car, test if it collides with anything else (ground, other cars or objects). +void CarCheckCollision(tGameEntity *carEntity) +{ + tGameEntity *entity=(tGameEntity*)gFirstEntity->next; + + //if(carEntity->physicsMachine==kPhysicsLocal) + { + // if(!gReplay) + + if(!(gFrameCount%kCarCollisionRate)) + { + if(carEntity->physicsMachine!=kPhysicsLocal) + gQuickRoadCollision=true; + gRoadRestrictedBorders=true; + CheckGroundCollision(carEntity); + gRoadRestrictedBorders=false; + gQuickRoadCollision=false; + } + } + + //for each other object, test for collision. + if(!(gFrameCount%kCarCollisionRate)) + while(entity!=gFirstEntity) + { + entity->regData=carEntity->regData; + if(sqr(entity->pos-carEntity->pos)<500) + if(entity!=carEntity) + switch(entity->physicsType) + { + case kPhysicsTypeSolid: + HandleCarSolidCollision(carEntity,entity); + break; + case kPhysicsTypeCar: + //if(carEntity->physicsMachine==kPhysicsLocal) + HandleCarCarCollision(carEntity,entity); + break; + } + entity=(tGameEntity*)entity->next; + } +} + +//for a solid entity, check if it intersects with the ground +void SolidCheckCollision(tGameEntity *entity) +{ + tSolidEntityPhysics *ent=(tSolidEntityPhysics*)FileGetParsedDataPtr(entity->physicsData,kParserTypeSolidEntityDesc,sizeof(tSolidEntityPhysics)); + if(!ent->movable) + return; + CheckGroundCollision(entity); + + tVector3 xDiff=*MatrixGetXVector(entity->rVelo)-Vector(1,0,0); + tVector3 yDiff=*MatrixGetYVector(entity->rVelo)-Vector(0,1,0); + tVector3 zDiff=*MatrixGetZVector(entity->rVelo)-Vector(0,0,1); + *MatrixGetXVector(entity->rVelo)=*MatrixGetXVector(entity->rVelo)-xDiff*1.5*kFrameTime; + *MatrixGetYVector(entity->rVelo)=*MatrixGetYVector(entity->rVelo)-yDiff*1.5*kFrameTime; + *MatrixGetZVector(entity->rVelo)=*MatrixGetZVector(entity->rVelo)-zDiff*1.5*kFrameTime; +} + +tRegData rd; + +//check for any object collisions +void CollisionFrame() +{ + #ifndef __TARGET_TOOLAPP + rd.name=RT3_GetLicenseName(); + rd.numCopies=RT3_GetLicenseCopies(); + #endif + tGameEntity *entity=(tGameEntity*)gFirstEntity->next; + while(entity!=gFirstEntity) + { + entity->regData=&rd; + //has the object moved in the last second? + //if we have a solid entity like a barrel which has been kicked over by a car, + //we don't need to process collisions with the ground anymore once the barrel + //has come to rest. + if(entity->lastActivity+kFPS>gFrameCount&&entity->id>=0) + //if(entity->physicsMachine!=kPhysicsRemote||entity->lastCollFrame==0||entity->lastCollFrame>gFrameCount-kFPS) + switch(entity->physicsType) + { + case kPhysicsTypeCar: + CarCheckCollision(entity); + break; + case kPhysicsTypeSolid: + if(!(gFrameCount%kSolidCollisionRate)) + SolidCheckCollision(entity); + break; + } + entity=(tGameEntity*)entity->next; + } + +} diff --git a/source/collision.h b/source/collision.h new file mode 100644 index 0000000..b1ca774 --- /dev/null +++ b/source/collision.h @@ -0,0 +1,15 @@ +#ifndef __COLLISION +#define __COLLISION + +#define kCarCollisionRate 1 +#define kSolidCollisionRate 5 + +#define kSolidEntityNetworkMass 100 + +void ApplyImpulse(tGameEntity *entity,tVector3 attackPoint,tVector3 veloDiff,float rotationFactor,int net); +float GetGroundOffset(tVector3 point,int *lastRoadIndex,tVector3 *normal,int *surfaceType); +float GetGroundOffsetAndBump(tVector3 point,int *lastRoadIndex,tVector3 *normal,int *surfaceType,float *bump); +void SolidCheckCollision(tGameEntity *entity); +void CollisionFrame(); + +#endif \ No newline at end of file diff --git a/source/config.cpp b/source/config.cpp new file mode 100644 index 0000000..7dfb97d --- /dev/null +++ b/source/config.cpp @@ -0,0 +1,41 @@ +#include "fileio.h" +#include "config.h" +#include "gamemem.h" +#include "parser.h" +#include "gameinitexit.h" +#include "carselection.h" +#include "screen.h" +#include + +tSysConfig *gConfig; + +void ConfigInit() +{ + gConfig=(tSysConfig*)FileGetParsedDataPtr(FileGetReference(kConfigFileName),kParserTypeConfigDesc,sizeof(tSysConfig)); + if(strlen(gConfig->playerName)==0) + CFStringGetCString(CSCopyUserName(false),gConfig->playerName,256,0); + if(strlen(gConfig->gameName)==0) + CFStringGetCString(CSCopyMachineName(),gConfig->gameName,256,0); + if(strlen(gConfig->playerName)>=kMaxNameLength) + gConfig->playerName[kMaxNameLength-1]='\0'; + if((gConfig->dbIndex^gConfig->challengeData)!=0xdeadbeef) + { + gConfig->dbIndex=0xdeadbeef; + gConfig->challengeData=0; + } + + int availableCars[kMaxCars]; + int carCount; + GetAvailableCars(availableCars,&carCount,false,false); + for(int i=0;i<11;i++) + { + int ok=false; + for(int j=0;jopponentCars[i]) + ok=true; + if(!ok) + gConfig->opponentCars[i]=availableCars[0]; + } + if(ScreenNoWindow()) + gConfig->fullscreen=true; +} \ No newline at end of file diff --git a/source/config.h b/source/config.h new file mode 100644 index 0000000..d54e86b --- /dev/null +++ b/source/config.h @@ -0,0 +1,108 @@ +#ifndef __CONFIG +#define __CONFIG + +#include "fileio.h" + +enum{ + kInteriorDisplayOff, + kInteriorDisplayPlayerOnly, + kInteriorDisplayCloseCarsOnly, + kInteriorDisplayAlways, + kInteriorDisplayNumModes +}; + +typedef struct{ + int keyID; + int controllerID1; + int controllerID2; + int elementID; + char identifier[32]; + char controllerIdentifier[256]; +}tKeyConfig; + +typedef struct{ + int axisControllerID1; + int axisControllerID2; + int axisElementID; + char axisIdentifier[256]; + int min,mid,max; + float deadzone; +}tAxisConfig; + +#define kMaxPersonalRecords 1024 +typedef struct{ + tFileRef car,map; + int mode,direction; + int time; +}tPersonalRecord; + +typedef struct{ + int screenXSize,screenYSize; + int windowX,windowY; + int fullscreen; + int onlyRegisteredPlayers; + int useBetaBuilds; + int noGhost; + float gfxDynamics; + int allowHugeGames; + int interfaceSounds; + int stencil; + int performanceStats; + int textureQuality; + int soundEnable; + int maxPlayers; + int color32Bit; + int fsaa; + int guideSigns; + int motionBlur; + int allCams; + int cantGoBackwards; + int ffb; + float ffbIntensity; + int arcade,reverse; + int showPlayerNames; + int trackerEnable; + int carsOnSpeed; + int demolition; + int registerLapTimes; + int metricUnits; + int challengeData; + int dbIndex; + int textureFilter; + int showReplays; + int seperateGasBrake; + int disableAnalogueTCS; + int reverseGas; + tFileRef lastCar,lastEnemy,lastRoad,lastEnv; + int numEnemies,automatic,lastLaps,lastColor; + float soundVolume,musicVolume; + int maxCarSources; + float hudTransparency; + int interiorDisplay; + int cameraMode; + int numKeys,numAxis,numTaunts; + char playerName[256]; + char gameName[256]; + char password[256]; + char (*taunts)[256]; + char confirmedVersion[256]; + tKeyConfig *keys; + tAxisConfig *axis; + tFileRef opponentCars[11]; + int opponentColors[11]; + int challengeRecords[32]; + int numPersonalRecords; + tPersonalRecord records[kMaxPersonalRecords]; +}tSysConfig; + +extern tSysConfig *gConfig; + +#define kConfigDefault16Name "default16.cfg" +#define kConfigDefault32Name "default32.cfg" +#define kConfigDefault64Name "default64.cfg" +#define kConfigFileName "Redline Preferences" +#define kConfigFilePName "\pRedline Preferences" + +void ConfigInit(); + +#endif \ No newline at end of file diff --git a/source/controls.cpp b/source/controls.cpp new file mode 100755 index 0000000..365e303 --- /dev/null +++ b/source/controls.cpp @@ -0,0 +1,740 @@ +#include +#include +#include +#include "entities.h" +#include "gameframe.h" +#include "carphysics.h" +#include "controls.h" +#include "config.h" +#include "renderframe.h" +#include "gameinitexit.h" +#include "particles.h" +#include "gametime.h" +#include "network.h" +#include "random.h" +#include "reg_tool_3.h" +#include "rt3_redline.h" +#include "initexit.h" +#include "text.h" + +#define kThrottleTime 0.3 +#define kThrottleReleaseTime 0.2 +#define kThrottleTCRReleaseTime 0.7 +#define kThrottleTimeSpinning 0.6 +#define kBrakeTime 0.4 +#define kBrakeReleaseTime 0.2 +#define kSteerReleaseTime 0.6 +#define kVeloSteerReleaseTime 0 +//#define kSteerTime 1.2 +#define kSteerTime 0.6 +#define kVeloSteerTime 0.05 +#define kHandbrakeTime 0.2 +#define kHandbrakeReleaseTime 0.01 + +#define sign(x) ((x)<0?-1:1) + +#define kMaxThottleSlip 0.033 +#define kMinSpinAngularVelo (4*PI) +#define kMaxBrakeSlip 0.05 + +int gGearUpPress=false; +int gGearDownPress=false; +int gLightPress=false; +int gCameraPress=false; +int gReplayCameraPress=false; +int gTauntPress=false; +int gArrowPress=false; +int gEscPress=false; +int gPausePress=false; + +float gSteerAxis=0; +float gThrottleAxis=0; +int gAxisSteering=false; +int gAxisThrottleBrake=false; +int gInputChatMode=false; +int gInputEscMode=kInputEscModeNone; +int gInputEscSelection; +int gGameChatMessageSize=0; + +#define kMaxGameChatMessageSize 128 +char gGameChatMessage[kMaxGameChatMessageSize]=""; + +void ButtonThrottle(tCarDefinition *car,tCarPhysics *phys,float driveSlip,float angularVelo,float velo,float throttleInput,int tcr) +{ + float maxSlip=velo<20?0.2-velo/20*(0.2-kMaxThottleSlip):kMaxThottleSlip; + if(throttleInput<0) + throttleInput=0; + if(throttleInput>phys->throttle){ + if(driveSlip*sign(phys->gear)clutch<1&&phys->rpm>car->clutchRPM)) + { + phys->throttle+=kFrameTime*(1/kThrottleTime); + if(throttleInput>phys->throttle) + throttleInput=phys->throttle; + } + else + { + phys->throttle-=kFrameTime*(1/kThrottleTCRReleaseTime); + if(throttleInputthrottle) + throttleInput=phys->throttle; + } + } + else { + phys->throttle-=kFrameTime*(1/kThrottleReleaseTime); + if(throttleInputthrottle) + throttleInput=phys->throttle; + } + + if(phys->throttle>1) + phys->throttle=1; + else if(phys->throttleidleThrottle&&phys->rpm<=car->idleRPM) + phys->throttle=phys->idleThrottle; + + phys->clutch=1; +} + + +void ButtonBrake(tCarPhysics *phys,float slip,float angularVelo,float brakeInput,int als) +{ + if(brakeInput>1) + brakeInput=1; + else if(brakeInput<0) + brakeInput=0; + + if(brakeInput<0) + brakeInput=0; + if(brakeInput>phys->brake) + { + if((-slip*sign(angularVelo)brake<0.1)||!als) + phys->brake+=kFrameTime*(1/kBrakeTime); + else + phys->brake-=kFrameTime*(1/kBrakeTime); + + phys->arcadeBrake+=kFrameTime*(1/kBrakeTime); + + if(brakeInputbrake) + phys->brake=brakeInput; + if(brakeInputarcadeBrake) + phys->arcadeBrake=brakeInput; + } + else + { + phys->brake-=kFrameTime*(1/kBrakeReleaseTime); + + phys->arcadeBrake-=kFrameTime*(1/kBrakeReleaseTime); + + if(brakeInput>phys->brake) + phys->brake=brakeInput; + if(brakeInput>phys->arcadeBrake) + phys->arcadeBrake=brakeInput; + } +} + +void CalcThrottleBrake(tCarDefinition *car,tCarPhysics *phys,float driveSlip,float angularVelo,float velo,float slip) +{ + if(car->clutchRPM==0) + car->clutchRPM=3000; + int raceOver=(phys->lapCount>gGameInfo->numLaps&&gGameInfo->numLaps!=-1); + if(!raceOver) + { + float gas,brake,gasbrake; + if(gConfig->seperateGasBrake) + { + if(gConfig->reverseGas) + { + gas=GetAxisInput(kInputThrottleAxis); + brake=GetAxisInput(kInputBrakeAxis); + } + else + { + gas=1-GetAxisInput(kInputThrottleAxis); + brake=1-GetAxisInput(kInputBrakeAxis); + } + gasbrake=gas-brake; + } + else + { + gasbrake=GetAxisInput(kInputThrottleBrakeAxis); + if(gConfig->reverseGas) + gasbrake=-gasbrake; + gas=gasbrake; + brake=-gasbrake; + } + + if(gAxisThrottleBrake) + { + + if(gConfig->automatic&&phys->gear==-1) + { + ButtonBrake(phys,slip,angularVelo,gas,true); + ButtonThrottle(car,phys,driveSlip,angularVelo,velo,brake,(brake<0.95)&&!gConfig->disableAnalogueTCS); + } + else + { + ButtonBrake(phys,slip,angularVelo,brake,true); + ButtonThrottle(car,phys,driveSlip,angularVelo,velo,gas,(gas<0.95)&&!gConfig->disableAnalogueTCS); + } + if(GetButtonInput(kInputKickdownButton)||GetButtonInput(kInputBrakeButton)||GetButtonInput(kInputGasButton)) + gAxisThrottleBrake=false; + } + else + { + int brake=GetButtonInput(kInputBrakeButton); + int gas=GetButtonInput(kInputGasButton); + int kickdown=GetButtonInput(kInputKickdownButton); + + int gasInput=(gConfig->automatic?(phys->gear>=0?gas||kickdown:brake):(gas||kickdown))?1:0; + ButtonThrottle(car,phys,driveSlip,angularVelo,velo,gasInput,!kickdown); + int brakeInput=(gConfig->automatic?(phys->gear>=0?brake:gas||kickdown):brake)?1:0; + ButtonBrake(phys,slip,angularVelo,brakeInput,true); + + if(gThrottleAxis!=gasbrake) + gAxisThrottleBrake=true; + } + gThrottleAxis=gasbrake; + } + else + { + ButtonBrake(phys,slip,angularVelo,1,true); + ButtonThrottle(car,phys,driveSlip,angularVelo,velo,0,true); + } +} + +void ButtonSteering(tGameEntity *entity,tCarDefinition *car,tCarPhysics *phys,float velo,float input) +{ + if(input>1) input=1; + else if(input<-1) input=-1; + + tVector3 carDir=!Vector(MatrixGetZVector(entity->dir)->x,0,MatrixGetZVector(entity->dir)->z); + tVector3 veloDir=!Vector(entity->velo.x,0,entity->velo.z); + float angleSin=(veloDir%carDir).y; + if(fabs(angleSin)>1.0) + angleSin=sign(angleSin); + float angle=-asin(angleSin); + float optimalSteering=angle/car->wheels[0].maxAngle; + if(velo<1) + optimalSteering=0; + + if(input>phys->steering) + { + float steerSpeed=(phys->steering<0)?(1/(kSteerReleaseTime+kVeloSteerReleaseTime*velo)) :(1/(kSteerTime+kVeloSteerTime*velo)); + if(phys->steeringsteering)*4; + if(gGameInfo->arcade==kGameModeTurbo) + steerSpeed*=2; + phys->steering+=kFrameTime*steerSpeed; + + if(inputsteering) + phys->steering=input; + } + else if(inputsteering) + { + float steerSpeed=(phys->steering>0)?(1/(kSteerReleaseTime+kVeloSteerReleaseTime*velo)) :(1/(kSteerTime+kVeloSteerTime*velo)); + if(phys->steering>optimalSteering) + steerSpeed*=1+(phys->steering-optimalSteering)*4; + if(gGameInfo->arcade==kGameModeTurbo) + steerSpeed*=2; + phys->steering-=kFrameTime*steerSpeed; + + if(input>phys->steering) + phys->steering=input; + } +} + +void CalcSteering(tGameEntity *entity,tCarDefinition *car,tCarPhysics *phys,float velo) +{ + if(gAxisSteering) + { + gSteerAxis=GetAxisInput(kInputSteerAxis); + float ax3=gSteerAxis*gSteerAxis*gSteerAxis; + float ax=0.3*gSteerAxis+0.7*ax3; + ButtonSteering(entity,car,phys,velo,ax); + + if(GetButtonInput(kInputSteerLeftButton)||GetButtonInput(kInputSteerRightButton)) + gAxisSteering=false; + } + else + { + float input=0; + int l=GetButtonInput(kInputSteerLeftButton); + int r=GetButtonInput(kInputSteerRightButton); + if(l&&r) + input=phys->steering; + else if(l) + input=1; + else if(r) + input=-1; + ButtonSteering(entity,car,phys,velo,input); + if(gSteerAxis!=GetAxisInput(kInputSteerAxis)) + gAxisSteering=true; + } +} + +typedef struct{ + char *name; + int numCopies; +}tRegData; + +void ControlEntityUserInput(tGameEntity *entity) +{ + if(entity->physicsType!=kPhysicsTypeCar) + return; + tCarPhysics *phys=(tCarPhysics*)entity->physics; + tCarDefinition *car=&(phys->car); + + int isValid=true; + float velo=~entity->velo; + float driveSlip=0,slip=0,angularVelo=0; + int wheelsOnGround=0; + int valid=true; + if((car->demoAvailable-car->numColors!=1)&&(entity==gCarEntities[gGameInfo->playerID])) + isValid=false; + + for(int i=0;inumWheels;i++) + { + if(phys->wheels[i].onGround) + { + driveSlip+=phys->wheels[i].slip*car->wheels[i].powered; + slip+=phys->wheels[i].slip; + wheelsOnGround++; + } + angularVelo+=phys->wheels[i].angularVelo*car->wheels[i].powered; + } + if((gMapInfo->demoAvailable+gMapInfo->numObjs)) + isValid=false; + + if(wheelsOnGround) + slip/=wheelsOnGround; + else + slip=0; + CalcThrottleBrake(car,phys,driveSlip,angularVelo,velo,slip); + CalcSteering(entity,car,phys,velo); + + if(!isValid) + if(entity->regData) + qRT3_LicenseTestApp2(phys->regCode,((tRegData*)(entity->regData))->name,((tRegData*)(entity->regData))->numCopies,isValid); + + if(GetButtonInput(kInputHandbrakeButton)) + phys->handbrake+=kFrameTime*(1/kHandbrakeTime); + else + phys->handbrake-=kFrameTime*(1/kHandbrakeReleaseTime); + if(phys->handbrake>1) + phys->handbrake=1; + else if(phys->handbrake<0) + phys->handbrake=0; + + if(!isValid) + if(entity->regData) + qRT3_LicenseTestBlock2(phys->regCode,((tRegData*)(entity->regData))->name,((tRegData*)(entity->regData))->numCopies,isValid); + + if(gFrameCount*kFrameTime>=kStartGameDelaySeconds) + { + if(gConfig->automatic) + { + if((gFrameCount-1)*kFrameTime>phys->lastGearSwitch+car->gearSwitchTime) + { + if(phys->gear>=1) + { + int shiftUp=false; + if(car->shiftUpRPMFix) + shiftUp=phys->rpm>car->shiftUpRPMFix; + else + shiftUp=phys->rpm>car->maxRPM; + + if(shiftUp&&phys->gearnumGears-2) + { + if(!GetButtonInput(kInputKickdownButton)&&phys->gear>0&&gFrameCount*kFrameTime>=kStartGameDelaySeconds+1) + { + phys->lastGear=phys->gear; + phys->gear++; + phys->lastGearSwitch=gFrameCount*kFrameTime; + if(gGameInfo->arcade==kGameModeTurbo) + phys->lastGearSwitch-=car->gearSwitchTime*0.5; + } + } + else if(phys->rpmshiftDownRPM&&phys->gear>1) + { + phys->lastGear=phys->gear; + phys->gear--; + phys->lastGearSwitch=gFrameCount*kFrameTime; + if(gGameInfo->arcade==kGameModeTurbo) + phys->lastGearSwitch-=car->gearSwitchTime*0.5; + } + } + + if(phys->gear==0||velo<1) + { + if(phys->gear<1&&((GetButtonInput(kInputKickdownButton)||GetButtonInput(kInputGasButton)))||(gAxisThrottleBrake&&gThrottleAxis>0)) + { + phys->lastGear=phys->gear; + phys->gear=1; + phys->lastGearSwitch=gFrameCount*kFrameTime; + } + else if(phys->gear>=0&&GetButtonInput(kInputBrakeButton)||(gAxisThrottleBrake&&gThrottleAxis<0)) + { + phys->lastGear=phys->gear; + phys->gear=-1; + phys->lastGearSwitch=gFrameCount*kFrameTime; + } + } + } + } + else + { + if(GetButtonInput(kInputGearUp)){ + if(phys->gearnumGears-2&&!gGearUpPress){ + phys->lastGear=phys->gear; + phys->gear++; + gGearUpPress=true; + phys->lastGearSwitch=gFrameCount*kFrameTime; + if(gGameInfo->arcade==kGameModeTurbo) + phys->lastGearSwitch-=car->gearSwitchTime*0.5; + } + } + else gGearUpPress=false; + + if(GetButtonInput(kInputGearDown)){ + if(phys->gear>-1&&!gGearDownPress){ + phys->lastGear=phys->gear; + phys->gear--; + gGearDownPress=true; + phys->lastGearSwitch=gFrameCount*kFrameTime; + if(gGameInfo->arcade==kGameModeTurbo) + phys->lastGearSwitch-=car->gearSwitchTime*0.5; + } + } + else gGearDownPress=false; + } + + float gearSwitchTime=phys->gear<=1?0.3:car->gearSwitchTime; + if(gFrameCount*kFrameTimelastGearSwitch+gearSwitchTime) + { + float switchTime=(gFrameCount*kFrameTime-phys->lastGearSwitch)/gearSwitchTime; + phys->clutch=switchTime; + if(phys->rpmclutchRPM) + phys->clutch=(phys->rpm-car->idleRPM*1.5)/(car->clutchRPM-car->idleRPM*1.5); + if(switchTime<0.5&&phys->gear>1) + if(phys->lastGear>phys->gear) + phys->throttle=0.7; + else + phys->throttle=phys->idleThrottle; + if(entity==gViewedEntity) + { + int invalid=false; + if(!invalid)qRT3_LicenseIsSameCode(phys->regCode,RT3_PIRATED_CODE_01,invalid); + if(!invalid)qRT3_LicenseIsSameCode(phys->regCode,RT3_PIRATED_CODE_02,invalid); + if(!invalid)qRT3_LicenseIsSameCode(phys->regCode,RT3_PIRATED_FAKE_03,invalid); + if(!invalid)qRT3_LicenseIsSameCode(phys->regCode,RT3_PIRATED_FAKE_04,invalid); + if(!invalid)qRT3_LicenseIsSameCode(phys->regCode,RT3_PIRATED_FAKE_05,invalid); + if(!invalid)qRT3_LicenseIsSameCode(phys->regCode,RT3_PIRATED_FAKE_06,invalid); + if(!invalid)qRT3_LicenseIsSameCode(phys->regCode,RT3_PIRATED_FAKE_07,invalid); + if(!invalid)qRT3_LicenseIsSameCode(phys->regCode,RT3_PIRATED_FAKE_08,invalid); + if(!invalid)qRT3_LicenseIsSameCode(phys->regCode,RT3_PIRATED_FAKE_09,invalid); + if(!invalid)qRT3_LicenseIsSameCode(phys->regCode,RT3_PIRATED_FAKE_10,invalid); + if(!invalid)qRT3_LicenseIsSameCode(phys->regCode,RT3_PIRATED_FAKE_11,invalid); + if(!invalid)qRT3_LicenseIsSameCode(phys->regCode,RT3_PIRATED_FAKE_12,invalid); + if(!invalid)qRT3_LicenseIsSameCode(phys->regCode,RT3_PIRATED_FAKE_13,invalid); + if(!invalid)qRT3_LicenseIsSameCode(phys->regCode,RT3_PIRATED_FAKE_14,invalid); + if(!invalid)qRT3_LicenseIsSameCode(phys->regCode,RT3_PIRATED_FAKE_15,invalid); + if(!invalid)qRT3_LicenseIsSameCode(phys->regCode,RT3_PIRATED_FAKE_16,invalid); + if(!invalid)qRT3_LicenseIsSameCode(phys->regCode,RT3_PIRATED_FAKE_17,invalid); + if(!invalid)qRT3_LicenseIsSameCode(phys->regCode,RT3_PIRATED_FAKE_18,invalid); + if(!invalid)qRT3_LicenseIsSameCode(phys->regCode,RT3_PIRATED_FAKE_19,invalid); + if(!invalid)qRT3_LicenseIsSameCode(phys->regCode,RT3_PIRATED_FAKE_20,invalid); + valid=!invalid; + } + } + else + if(phys->rpmclutchRPM) + phys->clutch=(phys->rpm-car->idleRPM*1.5)/(car->clutchRPM-car->idleRPM*1.5); + else + phys->clutch=1; + + if(phys->clutch<0) + phys->clutch=0; + } + + if(!isValid) + if(gFrameCount>kFPS*30) + phys->throttle=0; + + if(GetButtonInput(kInputHorn)) + phys->lightFlags|=kLightFlagHorn; + else + phys->lightFlags&=~kLightFlagHorn; + + if(!valid) + if(RandomProb(kFrameTime/car->gearSwitchTime*7)) + phys->gear=0; + + if(GetButtonInput(kInputLeftIndicator)) + phys->lightFlags|=kLightFlagLIndLight; + else if(GetButtonInput(kInputRightIndicator)) + phys->lightFlags|=kLightFlagRIndLight; + else + phys->lightFlags&=~(kLightFlagLIndLight+kLightFlagRIndLight); + + if(GetButtonInput(kInputLightButton)){ + if(!gLightPress) + { + gLightPress=true; + phys->lightFlags^=kLightFlagDriveLight; + } + }else gLightPress=false; +} + +void ControlEntityAIInput(tGameEntity *entity); + +extern float gEscDisplay; + +void HandleEscModeInput() +{ + if(GetInterfaceKey(kInterfaceKeyEsc)||GetInterfaceKey(kInterfaceKeyDelete)) + { + if(!gEscPress) + { + gInputEscMode=kInputEscModeNone; + gEscPress=true; + if(!gGameInfo->network) + UnPauseGame(); + } + } + else + gEscPress=false; + if(GetInterfaceKey(kInterfaceKeyDown)) + { + if(!gArrowPress) + { + gInputEscSelection--; + if(gInputEscSelection<0) + gInputEscSelection=(gGameInfo->network?1:2); + gArrowPress=true; + } + } + else if(GetInterfaceKey(kInterfaceKeyUp)) + { + if(!gArrowPress) + { + gInputEscSelection=(gInputEscSelection+1)%(gGameInfo->network?2:3); + gArrowPress=true; + } + } + else + gArrowPress=false; + if(GetInterfaceKey(kInterfaceKeyReturn)||GetInterfaceKey(kInterfaceKeyEnter)) + { + if(gInputEscSelection==1) + if(gInputEscMode==kInputEscModeQuit) + Exit(); + else + gGameEnd=kEndGame; + if(gInputEscSelection==2) + gGameEnd=kEndGameRestart; + if(gInputEscMode==kInputEscModeQuit) + gEscDisplay=0; + gInputEscMode=kInputEscModeNone; + if(!gGameInfo->network) + UnPauseGame(); + } +} + +void HandleChatModeInput() +{ + while(char ch=GetKeyInput(NULL)) + { + if(ch=='\r') + { + if(ch=='\r'&&strlen(gGameChatMessage)>0) + { + tChatMessage msg; + memset((void*)&msg,0,sizeof(tChatMessage)); + sprintf(msg.str,"%s: \255#a\255%s",gConfig->playerName,gGameChatMessage); + msg.flags=0; + NetworkSendPacket(kMessageTypeChat,&msg,sizeof(tChatMessage),kMessagePriorityHigh,kMessageSendToAll); + } + gGameChatMessage[0]='\0'; + gGameChatMessageSize=0; + gInputChatMode=false; + } + if((ch==0x7f||ch==0x08)&&gGameChatMessageSize>0) + gGameChatMessage[--gGameChatMessageSize]='\0'; + if(ch>=' '&&ch<='z'&&gGameChatMessageSizetaunts[i-kInputTaunt1]) + { + tChatMessage msg; + memset((void*)&msg,0,sizeof(tChatMessage)); + snprintf(msg.str,256,"%s: \255#a\255%s",gConfig->playerName,gConfig->taunts[i-kInputTaunt1]); + msg.flags=0; + NetworkSendPacket(kMessageTypeChat,&msg,sizeof(tChatMessage),kMessagePriorityHigh,kMessageSendToAll); + } + } + gTauntPress=tauntPress; + + + if(gInputChatMode) + HandleChatModeInput(); +} + +void HandleGameInput() +{ + tCarPhysics *phys=(tCarPhysics*)gCarEntities[gGameInfo->playerID]->physics; + + if(GetButtonInput(kInputCamera)) + { + if(!gCameraPress) + { + ParticlesClearScreen(); + gCameraMode=(++gCameraMode)%(gConfig->allCams||gReplay||phys->lapCount>gGameInfo->numLaps&&gGameInfo->numLaps!=-1?kCameraNumModes:kCameraLeftWheel); + gReplayAutoCam=false; + } + gCameraPress++; + if(gCameraPress>=kFPS&&gCameraMode!=kCameraFree) + { + gCameraMode=kCameraFree; + TextPrintfToBufferFormatedFading(Vector(0,1),0.03,kTextAlignMiddle,1,2,"freecam mode."); + } + } + else gCameraPress=0; + + if(GetButtonInput(kInputCameraChangeCar)){ + if(!gReplayCameraPress) + { + if(gReplay||phys->lapCount>gGameInfo->numLaps) + { + do{ + gReplayViewedEntityID=(gReplayViewedEntityID+1)%gGameInfo->numPlayers; + }while(gGameInfo->netID[gReplayViewedEntityID]==-1&&gGameInfo->network); + if(gReplay) + gReplayAutoCam=false; + gReplayCameraPress=true; + } + } + }else + gReplayCameraPress=false; + + if(gReplay&&!gGameInfo->network) + { + float oldStretch=gTimeStretch; + if(GetButtonInput(kInputChat)) + gTimeStretch=0.25; + else + gTimeStretch=1.0; + if(gTimeStretch!=oldStretch) + { + float curTime=gStartTime+(gFrameCount*kFrameTime)/oldStretch; + gStartTime=curTime-(gFrameCount*kFrameTime)/gTimeStretch; + } + } + gCameraReverse=GetButtonInput(kInputCameraReverse); + + + + if(GetButtonInput(kInputPause)&&!gReplay&&!gPaused&&!gPausePress) + { + if(!gGameInfo->network) + PauseGame(); + else + { + int pauseFrameCount=gFrameCount+kFPS*0.5; + S32Swap(pauseFrameCount); + NetworkSendPacket(kMessageTypePause,&pauseFrameCount,sizeof(int),kMessagePriorityHigh,kMessageSendToAll); + } + gPausePress=true; + } + + if(((GetButtonInput(kInputPause)&&!gPausePress))&&gPaused&&!gInputChatMode) + { + if(!gGameInfo->network) + UnPauseGame(); + else + NetworkSendPacket(kMessageTypeResume,NULL,0,kMessagePriorityHigh,kMessageSendToAll); + gPausePress=true; + } + + if(!GetButtonInput(kInputPause)) + gPausePress=false; + + if(gGameInfo->network) + HandleNetworkInput(); + +/* if(GetInterfaceKey(kInterfaceKeyCmd)&&GetInterfaceKey(kInterfaceKeyQ)) + { + gInputEscMode=kInputEscModeQuit; + gInputEscSelection=1; + if(!gGameInfo->network) + PauseGame(); + }*/ + + if(GetInterfaceKey(kInterfaceKeyEsc))//||(GetInterfaceKey(kInterfaceKeyDelete)&&!gInputChatMode)) + { + if(gInputChatMode) + { + gGameChatMessage[0]='\0'; + gGameChatMessageSize=0; + gInputChatMode=false; + gEscPress=true; + } + else if(!gEscPress) +// if(!gGameInfo->network&&((phys->lapCount>gGameInfo->numLaps&&gGameInfo->numLaps!=-1)||(gFrameCount*kFrameTime-kStartGameDelaySeconds>gGameInfo->maxTime&&gGameInfo->maxTime>0))) + if((gReplay||((phys->lapCount>gGameInfo->numLaps&&gGameInfo->numLaps!=-1)))&&(!gGameInfo->network||gGameInfo->playerID==0))//||(gFrameCount*kFrameTime-kStartGameDelaySeconds>gGameInfo->maxTime&&gGameInfo->maxTime>0))) + gGameEnd=kEndGame; + else + { + gEscPress=true; + gInputEscMode=kInputEscModeNormal; + gInputEscSelection=1; + if(!gGameInfo->network) + PauseGame(); + } + } + else + gEscPress=false; +} + +void ControlFrame() +{ + if(!gPaused&&!gReplay) + { + tGameEntity *entity=(tGameEntity*)gFirstEntity->next; + while(entity!=gFirstEntity) + { + if(entity->physicsMachine==kPhysicsLocal) + { + switch(entity->controlType) + { + case kControlTypeUserInput: + ControlEntityUserInput(entity); + break; + case kControlTypeAIInput: + ControlEntityAIInput(entity); + break; + } + } + entity=(tGameEntity*)entity->next; + } + } + + if(!gInputEscMode) + HandleGameInput(); + else + HandleEscModeInput(); +} \ No newline at end of file diff --git a/source/controls.h b/source/controls.h new file mode 100755 index 0000000..2f2a580 --- /dev/null +++ b/source/controls.h @@ -0,0 +1,100 @@ +#ifndef __CONTROLS +#define __CONTROLS + +#include "config.h" +#include "vectors.h" + +enum{ + kInputGasButton, + kInputBrakeButton, + kInputSteerLeftButton, + kInputSteerRightButton, + kInputHandbrakeButton, + kInputKickdownButton, + kInputGearUp, + kInputGearDown, + kInputLightButton, + kInputHorn, + kInputChat, + kInputCamera, + kInputPause, + kInputEscape, + kInputCameraReverse, + kInputTaunt1, + kInputTaunt2, + kInuptTaunt3, + kInputTaunt4, + kInputTaunt5, + kInputCameraChangeCar, + kInputITunesNext, + kInputITunesPrev, + kInputITunesPlay, + kInputLeftIndicator, + kInputRightIndicator, + kInputNumControls +}; + +enum{ + kInterfaceKeyNone, + kInterfaceKeyUp, + kInterfaceKeyDown, + kInterfaceKeyLeft, + kInterfaceKeyRight, + kInterfaceKeySpace, + kInterfaceKeyReturn, + kInterfaceKeyEnter, + kInterfaceKeyEsc, + kInterfaceKeyEasterEgg, + kInterfaceKeyDelete, + kInterfaceKeyCmd, + kInterfaceKeyOpt, + kInterfaceKeyS, + kInterfaceKeyR, + kInterfaceKeyQ, + kInterfaceMouseDown, + kInterfaceNumKeys +}; + +enum{ + kInputSteerAxis, + kInputThrottleBrakeAxis, + kInputThrottleAxis, + kInputBrakeAxis, + kInputNumAxis +}; + +enum{ + kInputEscModeNone, + kInputEscModeNormal, + kInputEscModeQuit +}; + +extern int gFFB; +extern int gInputHID; +extern int gInputChatMode; +extern int gInputEscMode; +extern int gInputEscSelection; +extern float gStartPauseTime; +extern char gGameChatMessage[]; +extern int gGameChatMessageSize; + +void ControlFrame(); +void ControlInit(); +void ControlExit(); +int GetButtonInput(int id); +int GetInterfaceKey(int id); +char GetKeyInput(int *key); +char PeekKeyInput(int *key); +float GetAxisInput(int id); +void GetInput(tKeyConfig *keyConfig); +void GetAxis(tAxisConfig *axis,bool allowVendor); +void FlushKeys(); +void FFBJolt(float lMag,float rMag,float duration); +void FFBiShockDirect(float lMag,float rMag); +void FFBSetSteerResistance(float res,float alignment); +void FFBSetGoundRumble(float velo, float rumble); +void FFBStop(); +void CalibrateAxis(int id); +tVector2 GetMousePos(); + +#endif \ No newline at end of file diff --git a/source/entities.cpp b/source/entities.cpp new file mode 100755 index 0000000..192a422 --- /dev/null +++ b/source/entities.cpp @@ -0,0 +1,23 @@ +#include "gamemem.h" +#include "entities.h" + +tGameEntity *gFirstEntity=NULL,*gCameraEntity,*gViewedEntity; +int gEntityID=0; + +void EntityResetCount() +{ + gEntityID=0; +} + +tGameEntity *EntityNew(tGameEntity *prev) +{ + tGameEntity *entity=(tGameEntity*)MemoryAllocateZeroedBlock(sizeof(tGameEntity)); + entity->next=prev->next; + entity->prev=prev; + ((tGameEntity*)(prev->next))->prev=entity; + prev->next=entity; + MatrixIdentity(entity->dir); + MatrixIdentity(entity->rVelo); + entity->id=gEntityID++; + return entity; +} diff --git a/source/entities.h b/source/entities.h new file mode 100755 index 0000000..c50793d --- /dev/null +++ b/source/entities.h @@ -0,0 +1,117 @@ +#ifndef __ENTITIES +#define __ENTITIES + +#include "vectors.h" +#include "fileio.h" + +enum{ + kRenderTypeNone=0, + kRenderTypeModel, + kRenderTypeCar, + kRenderTypeGhost +}; + +enum{ + kPhysicsTypeNone=0, + kPhysicsTypeCar, + kPhysicsTypeSolid, + kPhysicsTypeGhost +}; + +enum{ + kPhysicsNone=0, + kPhysicsLocal, + kPhysicsRemote +}; + +enum{ + kControlTypeNone=0, + kControlTypeUserInput, + kControlTypeAIInput +}; + +enum{ + kPathTypeCircular=0, + kPathTypeSin +}; + +typedef struct{ + tVector3 tfr,tfl,trl,trr,bfr,bfl,brl,brr; +} tCollBox; + + +enum{ + kLightTypeDot=0, + kLightTypeSpot, + kLightTypeDotRefelective, + kLightTypeDirectionlessDot, + kLightTypeDirectionlessDotReflective, + kLightTypeSpecularDot +}; + +typedef struct{ + tVector3 pos,dir,rgb; + float size,radius; + int type; + int onFlags; + int offFlags; +} tLightDefinition; + +typedef struct{ + int model; + int shadowModel; + float maxCollRadius; + int numCollBoxes; + int numColors; + int sparks; + int randomColor; + tCollBox *coll; + tVector3 massCenter; + int movable,followPath,pathType,liquid,particleStick; + tFileRef particle; + tFileRef sound; + int numSounds; + float particleAmount,particleSize; + float pathSize,pathVelo; + float mass,inertia; + int numLights; + tLightDefinition *lights; +} tSolidEntityPhysics; + +#define kNumAvgRVelos 32 +typedef struct{ + void *next,*prev; + tVector3 pos,velo,oldPos,netVelo,collVelo,netPos,accel; + tMatrix3 dir,rVelo,oldDir,netDir,netRVelo; + tMatrix3 lastRVelos[kNumAvgRVelos]; + tVector3 lastVelos[kNumAvgRVelos]; + tVector3 lastAccel[kNumAvgRVelos]; + tVector3 remoteCameraPos; + int renderType,renderData; + int physicsType,physicsData,physicsMachine; + int untouchable; + int controlType; + void *soundSource; + int id; + int lastFrame; + int lastRoadIndex; + int lastActivity; + int lastPacketSent; + tVector3 lastVeloSent; + tVector3 lastRZSent; + tVector3 lastDirZSent; + int lastPacketSaved; + tVector3 lastVeloSaved; + tVector3 lastRZSaved; + tVector3 lastDirZSaved; + float zDist; + void *physics; + void *regData; +} tGameEntity; + +extern tGameEntity *gFirstEntity,*gCameraEntity,*gViewedEntity; + +void EntityResetCount(); +tGameEntity *EntityNew(tGameEntity *prev); + +#endif \ No newline at end of file diff --git a/source/environment.cpp b/source/environment.cpp new file mode 100644 index 0000000..aca8cfd --- /dev/null +++ b/source/environment.cpp @@ -0,0 +1,28 @@ +//environment.cpp +//load a new environment fiel from disk +//environments basically control the wheather and +//what background images to use and stuff like that +#include "fileio.h" +#include "environment.h" +#include "gamemem.h" +#include "parser.h" +#include "roads.h" +#include "textures.h" +#include "gameinitexit.h" + +tEnvironment *gEnvironment; + +#define kAirDensity 1.2929 //kg*m^-3 +#define kGravitionalAcceleration 9.80665 //m*s^-2 + +void LoadEnvironment(int ref) +{ + gEnvironment=(tEnvironment*)FileGetParsedDataPtr(ref,kParserTypeEnvironmentDesc,sizeof(tEnvironment)); + gSurfaceTypes=(tSurfaceTypeList*)FileGetParsedDataPtr(gEnvironment->surfaceTypes,kParserTypeSurfaceTypeDesc,sizeof(tSurfaceTypeList)); + if(gEnvironment->gravity==0) + gEnvironment->gravity=kGravitionalAcceleration; + if(gEnvironment->airDensity==0) + gEnvironment->airDensity=kAirDensity; + if(gEnvironment->hasAltEnv&&gMapInfo->useAltEnv) + LoadEnvironment(gEnvironment->altEnv); +} \ No newline at end of file diff --git a/source/environment.h b/source/environment.h new file mode 100644 index 0000000..5925dfe --- /dev/null +++ b/source/environment.h @@ -0,0 +1,36 @@ +//environment.h + +#ifndef __ENVIRONMENT +#define __ENVIRONMENT + +#include "vectors.h" +#include "fileio.h" +enum{ + kEnvironmentMappingOff, + kEnvironmentMappingSphereAddSigned, + kEnvironmentMappingSphereInterpolate +}; + +typedef struct{ + tFileRef sky0,sky90,sky180,sky270,skytop,skybot,particlesType,surfaceTypes; + tFileRef spheremap,dirtMap; + tFileRef soundLoop,soundRandom; + tVector3 fogColor,ambient,diffuse,specular,shadowColor,spotLightColor,instrumentColor,flashColor; + tVector3 lightDir,particlesVelo,flareDir; + tVector2 particlesSize; + float particlesHeight; + float shadowIntensity,particlesAmount,particlesVeloSpread,particlesLife,flashIntensity,soundRandomProbility,environmentMapIntensity,dirtIntensity; + float gravity,airDensity; + int shadowEnable,spotLightEnable,particlesEnable,screenParticlesEnable,soundLoopEnable,soundRandomEnable,flaresEnable,dirtEnable; + int environmentMapping; + int envFlags; + char name[80]; + int hasAltEnv; + tFileRef altEnv; +} tEnvironment; + +extern tEnvironment *gEnvironment; + +void LoadEnvironment(int ref); + +#endif \ No newline at end of file diff --git a/source/error.h b/source/error.h new file mode 100644 index 0000000..204eb27 --- /dev/null +++ b/source/error.h @@ -0,0 +1 @@ +#ifndef __ERROR #define __ERROR void FailWithErrorString(char *string); void PrintConsoleString(const char *fmt, ...); void ShowAlert(char *str1, char *str2); void HandleError(int code); #endif \ No newline at end of file diff --git a/source/fileio.cpp b/source/fileio.cpp new file mode 100755 index 0000000..c4159b2 --- /dev/null +++ b/source/fileio.cpp @@ -0,0 +1,358 @@ +//fileio.h +//Utilities for reading and writing to files in the game's subdirectories + + +//fileio works by creating a table of all files within the applications directory +//at startup. this way all files we need can be addressed simply using reference numbers. +//sometimes also refered to as "ID numbers". + +#include +#include +#include +#include +#include "fileio.h" +#include "platform.h" +#include "gamemem.h" +#include "error.h" +#include "parser.h" + +int gFileTableSize; //size of the file reference table +int gFileTableExtendedSize; +int gFileTampered=false; +tFileTableEntry *gFileTable; //the file reference table +char gNoName[]=""; + +void FileInitFileTable(tFileTableEntry *fileTable,int maxSize,int *fileTableSize,int reInit); + +#define kNumValidateFiles 42 + +char gValidateNames[][256]={ + "500gt.car", + "500gthotrod.car", + "959.car", + "maserati.car", + "bmw2002.car", + "charger.car", + "chargerdragdrag.car", + "corvette.car", + "diablo.car", + "dmc12.car", + "dmc12flying.car", + "gt40.car", + "gti.car", + "mini.car", + "55ford.car", + "tt.car", + "ttpimp.car", + "viper.car", + "viperracer.car", + "accelbrake.mapinfo", + "accelbrake2.mapinfo", + "canyon.mapinfo", + "canyoncorner.mapinfo", + "city2.mapinfo", + "citycorner.mapinfo", + "downhill.mapinfo", + "highspeed.mapinfo", + "highspeedtrial.mapinfo", + "mountainside.mapinfo", + "offroad.mapinfo", + "quarter.mapinfo", + "ralley2.mapinfo", + "scorner.mapinfo", + "slalom.mapinfo", + "slalom2.mapinfo", + "snow.mapinfo", + "snowtrial.mapinfo", + "tight.mapinfo", + "canyon.road", + "city2.road", + "mountainside.road", + "ralley2.road", + "snow.road", +}; + +int gValidateChecksums[]={ + 0xc98d7f36, + 0x833e4a7d, + 0xdcd7e89, + 0x3e6a08b7, + 0x251efab, + 0xed2a6577, + 0x6d654b5, + 0x1c468570, + 0x1380a984, + 0x7c69956, + 0xcc2746e4, + 0x22349629, + 0x8d0d50b4, + 0x51994161, + 0x90a4cc85, + 0x931cbd6c, + 0xe42301b9, + 0xcc9bd572, + 0x1b7951e7, + 0x72d87975, + 0x1eb05d11, + 0xa2182ab9, + 0xde1122e6, + 0xc44b1aab, + 0x815cb099, + 0x8eb6b530, + 0x6c93ce2, + 0x77263bb7, + 0x18ebbbad, + 0xc57f8785, + 0x117ba80c, + 0xb513b806, + 0x75938236, + 0x2437d140, + 0xb249c233, + 0x6e92dc33, + 0x9e5c31de, + 0x8766b155, + 0x1bbdc55d, + 0x28f6a9b8, + 0x66e5492e, + 0x8b5a55d8, +}; +//Initialize file reference table +void FileInitIO() +{ + gFileTableExtendedSize=0; + gFileTableSize=0; + gFileTable=(tFileTableEntry*)calloc(kMaxFiles,sizeof(tFileTableEntry)); + FileInitFileTable(gFileTable,kMaxFiles,&gFileTableSize,false); + + for(int i=0;iname,((tFileTableEntry*)b)->name); +} + +int gFileErrorReporting=true; + +//Search file reference table for a file matching a name and return its reference number +tFileRef FileGetReference(char *name) +{ + if(*name=='\0') + return -1; + //the key to search for + tFileTableEntry key; +#ifndef __TARGET_TEXTURECOMPRESSOR +#ifndef __TARGET_TOOLAPP + snprintf(key.name,32,"%s.txr",name); + + //search for an entry matching our search key + tFileTableEntry *result=(tFileTableEntry*)bsearch(&key,gFileTable,gFileTableSize,sizeof(tFileTableEntry),&FileTableCompare); + + //return results + if(result) + return result-gFileTable; + else + { + snprintf(key.name,32,"%s.ima",name); + + //search for an entry matching our search key + tFileTableEntry *result=(tFileTableEntry*)bsearch(&key,gFileTable,gFileTableSize,sizeof(tFileTableEntry),&FileTableCompare); + + //return results + if(result) + return result-gFileTable; + else + + { +#endif +#endif + strcpy(key.name,name); + tFileTableEntry *result=(tFileTableEntry*)bsearch(&key,gFileTable,gFileTableSize,sizeof(tFileTableEntry),&FileTableCompare); + if(result) + return result-gFileTable; + else + { + for(int i=gFileTableSize;i0) + { + char nameCopy[kMaxFileNameLength]; + strcpy(nameCopy,FileGetName(base)); + char *found=strchr(nameCopy,'.'); + + char indexedName[kMaxFileNameLength]; + if(found) + { + *found='\0'; + sprintf(indexedName,"%s#%x.%s",nameCopy,i,found+1); + } + else + sprintf(indexedName,"%s#%x",nameCopy,i); + + int seek=base-1; + while(_stricmp(gFileTable[seek].name,indexedName)>0&&seek>0) + seek--; + + if(_stricmp(gFileTable[seek].name,indexedName)) + return base; + else + return seek; + } + else + return base; +} + +tFileRef FileGetBaseReference(tFileRef ref) +{ + if(ref<0||ref>=gFileTableExtendedSize+gFileTableSize) + return kFileErr; + char *found=strchr(FileGetName(ref),'.'); + if(!found) + return ref; + + char *cross=found; + while(*cross!='#'&&cross>FileGetName(ref)) + cross--; + if(*cross!='#') + return ref; + + int l=cross-FileGetName(ref); + char nameCopy[kMaxFileNameLength]; + memcpy(nameCopy,FileGetName(ref),l); + strcpy(nameCopy+l,found); + tFileRef base=FileGetReference(nameCopy); + return base!=kFileErr?base:ref; +} + +//platform specific, can be found in macfileio.cpp +int FileLoadData(tFileRef fileRef); +void FileStoreData(tFileRef fileRef); + +//return the extension of a file given it's reference number +char *FileGetExtension(tFileRef reference) +{ + if(reference<0||reference>=gFileTableExtendedSize+gFileTableSize) + return NULL; + char *found=NULL; + char *dot=gFileTable[reference].name; + do{ + dot=strchr(dot,'.'); + if(dot){ + found=dot; + dot++; + } + }while(dot); + if(found) + return found; + else + return gFileTable[reference].name+strlen(gFileTable[reference].name);//'\0'; +} + +//return the name of a file given it's reference number +char *FileGetName(tFileRef reference) +{ + if(reference<0||reference>=gFileTableExtendedSize+gFileTableSize) + return gNoName; + return gFileTable[reference].name; +} + +//return the size of a file given it's reference number +int FileGetSize(tFileRef reference) +{ + if(reference<0||reference>=gFileTableExtendedSize+gFileTableSize) + return 0; + if(!gFileTable[reference].loaded) + gFileTable[reference].loaded=FileLoadData(reference); + return gFileTable[reference].size; +} + +//return a pointer to the file's data (and load the file if necessary) +void *FileGetDataPtr(tFileRef reference) +{ + if(reference<0||reference>=gFileTableExtendedSize+gFileTableSize) + return NULL; + + if(!gFileTable[reference].loaded) + gFileTable[reference].loaded=FileLoadData(reference); + return gFileTable[reference].loaded?gFileTable[reference].data:(void*)0; +} + +unsigned int FileGetChecksum(tFileRef reference) +{ + if(reference<0||reference>=gFileTableExtendedSize+gFileTableSize) + return 0; + return MemoryChecksum(FileGetDataPtr(reference),FileGetSize(reference)); +} + +//Changes a file's contents to data, and writes the file to disk. +void FileSetData(tFileRef reference,void *data) +{ + if(reference<0||reference>=gFileTableExtendedSize+gFileTableSize) + return; + if(gFileTable[reference].loaded&&data!=gFileTable[reference].data) + MemoryFreeBlock(gFileTable[reference].data); + gFileTable[reference].data=data; + gFileTable[reference].loaded=true; + gFileTable[reference].size=MemoryBlockSize(data); + FileStoreData(reference); +} + +//parses a file's data using a file format parser +//and return a pointer to parsed data structure +void* FileGetParsedDataPtr(tFileRef reference, int parserType, int dataSize) +{ + if(reference<0||reference>=gFileTableExtendedSize+gFileTableSize) + return NULL; + if(!gFileTable[reference].parsed) + { + gFileTable[reference].parsedData=MemoryAllocateZeroedBlock(dataSize); + ParseFile(reference,gFileTable[reference].parsedData,parserType); + gFileTable[reference].parsed=true; + } + return gFileTable[reference].parsedData; +} \ No newline at end of file diff --git a/source/fileio.h b/source/fileio.h new file mode 100755 index 0000000..8a1fdde --- /dev/null +++ b/source/fileio.h @@ -0,0 +1,78 @@ +#ifndef __FILEIO +#define __FILEIO + + +#define kFileErr -1 +#define kMaxFiles 32768 +#define kMaxFileNameLength 32 + +typedef char tOpaqueFileSystemLocator[272]; //size of this is platform specific +typedef int tFileRef; + +typedef struct{ + int loaded; + int parsed; + int tagged; + int size; + void *data; + void *parsedData; + tOpaqueFileSystemLocator fsLoc; + char name[kMaxFileNameLength]; +} tFileTableEntry; + +#define kFileTypeRAW ".raw" +#define kFileTypeRAW3d ".raw3d" +#define kFileTypeGrayscaleRAW ".graw" +#define kFileTypeSGI ".sgi" +#define kFileTypeWaveFront ".obj" +#define kFileTypeWAV ".wav" +#define kFileTypeModel ".mdl" +#define kFileTypeSolidEntity ".ent" +#define kFileTypeCarDefinition ".car" +#define kFileTypeMapDefinition ".mapinfo" +#define kFileTypeEnvironmentDefinition ".env" +#define kFileTypePackageFile ".redplug" +#define kFileTypeImageURL ".url" +#define kFileTypeCompressedTexture ".txr" +#define kFileTypeLog ".redlog" + +extern int gFileTableSize; +extern int gFileTampered; +extern tFileTableEntry *gFileTable; + +static inline int _stricmp(const char *s1, const char *s2) +{ + char c1, c2; + while (1) + { + c1 = *s1++; + c2 = *s2++; + if(c1>='A'&&c1<='Z')c1+=0x20; + if(c2>='A'&&c2<='Z')c2+=0x20; + if (c1 < c2) return -1; + if (c1 > c2) return 1; + if (c1 == 0) return 0; + } +} + +#include "parser.h" + +extern int gFileErrorReporting; + +tFileRef FileGetReference(char *name); +tFileRef FileGetIndexedReference(tFileRef base,int i); +tFileRef FileGetBaseReference(tFileRef ref); +void FileAppendData(tFileRef fileRef,void *data,int size); +void *FileGetPartialDataPtr(tFileRef fileRef,int offset,int size); +void *FileGetDataPtr(tFileRef reference); +void FileSetData(tFileRef reference,void *data); +void FileReleaseData(int reference); +void FileInitIO(); +void FileRescanDirectory(); +char *FileGetExtension(tFileRef reference); +char *FileGetName(tFileRef reference); +int FileGetSize(tFileRef reference); +void* FileGetParsedDataPtr(tFileRef reference, int parserType, int dataSize); +unsigned int FileGetChecksum(tFileRef reference); + +#endif \ No newline at end of file diff --git a/source/fpu_exc.c b/source/fpu_exc.c new file mode 100644 index 0000000..c7c4bc8 --- /dev/null +++ b/source/fpu_exc.c @@ -0,0 +1,33 @@ +#ifdef __ppc__ +#include +#include + +#ifndef SIG_HOLD +#define SIG_HOLD (void (*)(int))5 +#endif + +void EnableFPUExceptions() +{ + long resp; + if(Gestalt(gestaltSystemVersion,&resp)!=noErr) + return; + if(resp<0x00001040) + return; + ppc_fp_scr_t fpscr=get_fp_scr(); + fpscr.ze=1; + fpscr.ni=1; + +/* fpscr.oe=1; + fpscr.ue=1; + fpscr.ve=1;*/ + + set_fp_scr(fpscr); + feclearexcept(FE_ALL_EXCEPT); + signal(SIGFPE,SIG_HOLD); + printf("FPU Exceptions enabled.\n"); +} +#else +void EnableFPUExceptions() +{ +} +#endif \ No newline at end of file diff --git a/source/fpu_exc.h b/source/fpu_exc.h new file mode 100644 index 0000000..9bb42f1 --- /dev/null +++ b/source/fpu_exc.h @@ -0,0 +1,4 @@ +extern "C" +{ +void EnableFPUExceptions(); +} diff --git a/source/gameframe.cpp b/source/gameframe.cpp new file mode 100755 index 0000000..ccc16a7 --- /dev/null +++ b/source/gameframe.cpp @@ -0,0 +1,846 @@ +//gameframe.cpp +//Calculate a single physics frame + +#include +#include +#include "gametime.h" +#include "entities.h" +#include "carphysics.h" +#include "gameframe.h" +#include "controls.h" +#include "gamemem.h" +#include "particles.h" +#include "renderframe.h" +#include "collision.h" +#include "networkphysics.h" +#include "gamesound.h" +#include "gameinitexit.h" +#include "config.h" +#include "parser.h" +#include "roads.h" +#include "gamesystem.h" +#include "interfaceutil.h" +#include "text.h" +#include "random.h" +#include "log.h" +#include "sky.h" +#include "tracker.h" +#include "environment.h" + +#define kGraphFrameTimeCount 10 //number of frames used to calculate average FPS + +int gFrameCount; //number of physics frames calculated +int gGraphFrameCount; //number of graphical frames actually drawn +int gPaused=false; +int gNetPauseTime=0; +int gDisabledRestart=0; +float gStartPauseTime; +float gStartTime; //time at the start of the game (in seconds) +float gTimeStretch=1.0; + +//Used for Time Trial +int gCurrentLapStart; +int gBestLapStart; +int gLastLapTime; +int gBestLapTime=0; +int gWorldRecord=0,gLocalRecord=0; +char gRecordName[255]; + +float gAccelSignDisplayIntensity,gAccelSignDisplayCorner; + +//the times when the last graphics frames have been draw +float gLastGraphFrameTime[kGraphFrameTimeCount]; + +int gGameEnd; //flag to signal end of game +int gDisqualified; +int gRaceFinished; //flag to signal that at least one player has finished all laps. +float gFPS; + +//Calculate physics for a solid entity +//(basically any object in the game which is not a car) +void SolidPhysicsEntity(tGameEntity *entity) +{ + //get soldid entity definition + tSolidEntityPhysics *ent=(tSolidEntityPhysics*)FileGetParsedDataPtr(entity->physicsData,kParserTypeSolidEntityDesc,sizeof(tSolidEntityPhysics)); + + //is this entity freely movable? + if(ent->movable) + //gravitational acceleration + entity->velo.y-=gEnvironment->gravity*kFrameTime; + + float dampfactor=0; + float v=~entity->velo; + if(v<7)dampfactor=(7-v)/7; + *MatrixGetXVector(entity->rVelo)=Vector(1,0,0)+(1-dampfactor*kFrameTime)*(*MatrixGetXVector(entity->rVelo)-Vector(1,0,0)); + *MatrixGetYVector(entity->rVelo)=Vector(0,1,0)+(1-dampfactor*kFrameTime)*(*MatrixGetYVector(entity->rVelo)-Vector(0,1,0)); + *MatrixGetZVector(entity->rVelo)=Vector(0,0,1)+(1-dampfactor*kFrameTime)*(*MatrixGetZVector(entity->rVelo)-Vector(0,0,1)); + entity->velo=(1-dampfactor*kFrameTime)*entity->velo; + MatrixReAdjust(entity->rVelo); + + //does this entity follow a constant path? + if(ent->followPath) + if(ent->pathType==kPathTypeCircular) + { + MatrixRotY(entity->dir,ent->pathVelo*kFrameTime); + entity->velo=*MatrixGetZVector(entity->dir)*ent->pathVelo*ent->pathSize; + } + else + { + entity->velo=Vector( + 3*sin(gFrameCount*kFrameTime*ent->pathVelo*0.2+1)*ent->pathVelo*0.2, + ent->pathSize*sin(gFrameCount*kFrameTime*ent->pathVelo)*ent->pathVelo, + 2*sin(gFrameCount*kFrameTime*ent->pathVelo*0.3-2)*ent->pathVelo*0.3 + ); + entity->lastActivity=gFrameCount; + } + +} + + +//Calculate one physics frame for all game entities +void PhysicsFrame() +{ + //do this for every entity in the game + tGameEntity *entity=(tGameEntity*)gFirstEntity->next; + while(entity!=gFirstEntity) + { + if(entity->id>=0) + { + //is this entity handled by this machine? + if(entity->physicsMachine==kPhysicsLocal) + { + //this check is used to prevent entitys which are just standing still + //from eating up CPU cycles + if(entity->lastActivity+kFPS>gFrameCount) + { + + //handle entity physics + switch(entity->physicsType) + { + case kPhysicsTypeCar: + CarPhysicsEntity(entity); + break; + case kPhysicsTypeSolid: + SolidPhysicsEntity(entity); + break; + } + + //Matrices tend to 'drift' apart du to inaccurate + //floating-point operations. these routines test matrices + //and re-adjust them if necessary. + if(!MatrixVerify(entity->dir)) + MatrixReAdjust(entity->dir); + if(!MatrixVerify(entity->rVelo)) + MatrixReAdjust(entity->rVelo); + } + } + else if(entity->physicsType==kPhysicsTypeCar) + CarPhysicsEntity(entity); + } + //proceed to next entity. + entity=(tGameEntity*)entity->next; + } +} + +//Move around all the entities. +void MotionFrame() +{ + //do this for every entity in the game + tGameEntity *entity=(tGameEntity*)gFirstEntity->next; + while(entity!=gFirstEntity) + { + entity->oldPos=entity->pos; + MatrixCopy(entity->dir,entity->oldDir); + + //this check is used to prevent entitys which are just standing still + //from eating up CPU cycles + if(entity->lastActivity+kFPS>gFrameCount) + { + //move entity + switch(entity->physicsType) + { + case kPhysicsTypeCar: + case kPhysicsTypeGhost: + CarMotionEntity(entity); + break; + case kPhysicsTypeSolid: + entity->pos=entity->pos+entity->velo*kFrameTime; + MatrixMult(entity->dir,entity->rVelo,entity->dir); + break; + } + } + + //Check if the entity is moving, and update lastActivity + if(entity->velo*entity->velo>kMinActivityVelo*kMinActivityVelo) + entity->lastActivity=gFrameCount; + + //proceed to next entity. + entity=(tGameEntity*)entity->next; + } +} + + + +//checks whether there is time to draw a graphics frame +//and calculates frame rate +int CheckFrameTime() +{ + int optFrameCount; + int retval=false; + float curTime; + + //get current time + curTime=(TimeGetSeconds()-gStartTime)*gTimeStretch; + + //calculate number of frames we should have calculated in that time + optFrameCount=curTime*kFPS; + + //if we have calculated more frames than required, we have + //time to draw a screen update. + if(gFrameCount>optFrameCount) + { + //calculate frame rate. + if(curTime-gLastGraphFrameTime[0]>0) + gFPS=((float)kGraphFrameTimeCount)/(curTime-gLastGraphFrameTime[0]); + else + gFPS=0; + MemoryMove(gLastGraphFrameTime,gLastGraphFrameTime+1,sizeof(float)*(kGraphFrameTimeCount-1)); + gLastGraphFrameTime[kGraphFrameTimeCount-1]=curTime; + gGraphFrameCount++; + retval=true; + } + if(gFPS<10) + gConfig->gfxDynamics-=kFrameTime; + else if(gFPS<15) + gConfig->gfxDynamics-=kFrameTime*0.3; + if(gFPS>35) + gConfig->gfxDynamics+=kFrameTime; + else if(gFPS>25) + gConfig->gfxDynamics+=kFrameTime*0.3; + if(gConfig->gfxDynamics<0.0)gConfig->gfxDynamics=0; + if(gConfig->gfxDynamics>1.0)gConfig->gfxDynamics=1; + return retval; +} + +//after drawing a graphics frame, check whether there is still time +//left. in that case wait, so we aren't running too fast. +void CheckTimeSkip() +{ + int optFrameCount; + float curTime; + + curTime=(TimeGetSeconds()-gStartTime)*gTimeStretch; + optFrameCount=curTime*kFPS; + while(gFrameCount>optFrameCount+1) + { + curTime=(TimeGetSeconds()-gStartTime)*gTimeStretch; + optFrameCount=curTime*kFPS; + } +} + +//start the frame counters +void StartFrameCount() +{ + gStartTime=TimeGetSeconds(); + gFrameCount=0; + gGraphFrameCount=0; + gCurrentLapStart=0; + gBestLapStart=0; + gLastLapTime=0; + gBestLapTime=0; + gWorldRecord=-1; + if(gGameInfo->numLaps==-1||gGameInfo->maxTime!=0) + TrackerFetchRecord(gGameInfo->map,gGameInfo->playerCars[0],gGameInfo->arcade,gGameInfo->reverse^gMapInfo->reverse); +} + +void PauseGame() +{ + if(!gPaused) + { + gPaused=true; + gStartPauseTime=TimeGetSeconds(); + SoundPause(); + FFBStop(); + } +} + +void UnPauseGame() +{ + if(gPaused) + { + gPaused=false; + gStartTime+=TimeGetSeconds()-gStartPauseTime; + SoundReInit(); + } +} + +void ResetRocks() +{ + int id=0; + for(int i=0;inumObjs;i++) + { + if(gMapInfo->obj[i].envFlags==0||(gMapInfo->obj[i].envFlags&gEnvironment->envFlags)) + { + char *extension=FileGetExtension(gMapInfo->obj[i].model); + if(extension) + if(!_stricmp(extension,kFileTypeSolidEntity)) + { + tGameEntity *entity=(tGameEntity*)gFirstEntity->next; + + //see if we find the entity it belongs to + while(entity!=gFirstEntity) + { + //is this the entity the message refers to? + if(entity->id==id) + { + entity->pos=gMapInfo->obj[i].pos; + EulerAnglesToMatrix(gMapInfo->obj[i].dir*kDegreeRadians,entity->dir); + entity->velo=Vector(0,0,0); + MatrixIdentity(entity->rVelo); + entity->lastActivity=-1000; + } + entity=(tGameEntity*)entity->next; + } + } + id++; + } + } +} + +//tests if entity has passed the next check-point +//(check-points are invisible, and only used to calculate +//lead and trail delays). +void CheckPointPass(tGameEntity *entity) +{ + tCarPhysics *phys=(tCarPhysics*)entity->physics; + if(phys->lap==-1) + return; + if(gGameInfo->numLaps!=-1) + { + if(!gCheckPoints[phys->checkPoint].passTimes[phys->lap].setBy) + { + gCheckPoints[phys->checkPoint].passTimes[phys->lap].time=gFrameCount*kFrameTime; + gCheckPoints[phys->checkPoint].passTimes[phys->lap].setBy=entity; + } + else + { + phys->lead=gCheckPoints[phys->checkPoint].passTimes[phys->lap].time-gFrameCount*kFrameTime; + if(!gCheckPoints[phys->checkPoint].passTimes[phys->lap].hit) + { + tCarPhysics *phys2=(tCarPhysics*)gCheckPoints[phys->checkPoint].passTimes[phys->lap].setBy->physics; + phys2->lead=gFrameCount*kFrameTime-gCheckPoints[phys->checkPoint].passTimes[phys->lap].time; + gCheckPoints[phys->checkPoint].passTimes[phys->lap].hit=true; + } + } + } + else + { + gCheckPoints[phys->checkPoint].passTimes[0].time=(gFrameCount-gCurrentLapStart)*kFrameTime; + if(phys->lapCount>1) + phys->lead=gCheckPoints[phys->checkPoint].passTimes[1].time-gCheckPoints[phys->checkPoint].passTimes[0].time; + } + + if(phys->checkPoint==0||(!gMapInfo->loop&&phys->checkPoint==kNumCheckPoints-1)) + { + if(!gReplay&&entity==gViewedEntity) + if(phys->lapCount&&(phys->lapCountnumLaps||gGameInfo->numLaps==-1)) + { + float lastLapTime; + if(gGameInfo->numLaps!=-1) + lastLapTime=gFrameCount-phys->lapTimes[phys->lapCount-1]; + else + lastLapTime=gFrameCount-gCurrentLapStart; + lastLapTime*=kFrameTime; + TextPrintfToBufferFormatedFading(Vector(0,0.4),0.04,kTextAlignMiddle,0.6,1.2,"Time Taken: %d:%02d'%02d",((int)lastLapTime)/60,((int)lastLapTime)%60,((int)(lastLapTime*100))%100); + } + + if(gGameInfo->numLaps!=-1) + phys->lapTimes[phys->lapCount]=gFrameCount; + else if(!gReplay){ + if(phys->lapCount>=1) + { + gLastLapTime=gFrameCount-gCurrentLapStart; + if(!gDisqualified) + { + TrackerRegisterLapTime(gGameInfo->map,gGameInfo->playerCars[0],gLastLapTime,gGameInfo->arcade,gGameInfo->reverse^gMapInfo->reverse); + if((gLastLapTimenumPersonalRecords;i++) + if(gConfig->records[i].map==gGameInfo->map&&gConfig->records[i].car==gGameInfo->playerCars[0] + &&gConfig->records[i].mode==gGameInfo->arcade&&gConfig->records[i].direction==gGameInfo->reverse) + { + found=true; + if(gConfig->records[i].time>gLocalRecord) + gConfig->records[i].time=gLocalRecord; + } + if(!found&&gConfig->numPersonalRecordsrecords[gConfig->numPersonalRecords].time=gLocalRecord; + gConfig->records[gConfig->numPersonalRecords].car=gGameInfo->playerCars[0]; + gConfig->records[gConfig->numPersonalRecords].map=gGameInfo->map; + gConfig->records[gConfig->numPersonalRecords].mode=gGameInfo->arcade; + gConfig->records[gConfig->numPersonalRecords].direction=gGameInfo->reverse; + gConfig->numPersonalRecords++; + } + } + LogToGhostLog(); + for(int i=0;imaxTime!=0&&!gDisqualified) + if(phys->lapCount>=1) + TrackerRegisterLapTime(gGameInfo->map,gGameInfo->playerCars[0],phys->lapTimes[phys->lapCount]-kStartGameDelaySeconds*kFPS,gGameInfo->arcade,gGameInfo->reverse^gMapInfo->reverse); + + phys->lapCount++; + + if(!gReplay&&entity==gViewedEntity&&gMapInfo->loop) + if(phys->lapCountnumLaps||gGameInfo->numLaps==-1) + TextPrintfToBufferFormatedFading(Vector(kFadingMessageXPos,kFadingMessageYPos),kFadingMessageSize,kTextAlignMiddle,0.9,1.8,"Lap %d",phys->lapCount); + else if(phys->lapCount==gGameInfo->numLaps) + TextPrintfToBufferFormatedFading(Vector(kFadingMessageXPos,kFadingMessageYPos),kFadingMessageSize,kTextAlignMiddle,0.9,1.8,"Final Lap"); + + if(gGameInfo->numLaps==-1) + ResetRocks(); + + if(phys->lapCount>gGameInfo->numLaps&&gGameInfo->numLaps!=-1) + { + if(!phys->finishTime) + phys->finishTime=phys->lapTimes[gGameInfo->numLaps]; + + if(gGameInfo->network) + if(gGameInfo->playerID==0) + for(int i=0;inumPlayers;i++) + if(gCarEntities[i]==entity) + { + tFinishTimeMessage m; + m.time=phys->finishTime; + m.player=i; + S32Swap(m.time); + S32Swap(m.player); + NetworkSendPacket(kMessageTypeFinishTime,&m,sizeof(tFinishTimeMessage),kMessagePriorityHigh,kMessageSendToAllButSelf); + } + + if(!gRaceFinished) + { + gRaceFinished=true; + if(entity!=gViewedEntity&&!gReplay) + if(gGameInfo->network) + { + for(int i=0;inumPlayers;i++) + if(gCarEntities[i]==entity) + TextPrintfToBufferFormatedFading(Vector(0,1),0.06,kTextAlignMiddle,2,3,"%s won the race!",gGameInfo->playerNames[i]); + } + else + TextPrintfToBufferFormatedFading(Vector(0,1),0.06,kTextAlignMiddle,2,3,"%s won the race!",phys->car.carName); + } + } + } + + phys->checkPoint=(phys->checkPoint+1)%kNumCheckPoints; +} + +void LapCheck() +{ + if(gGameInfo->demolition) + { + if(gFrameCount>=gDisabledRestart+kFPS*10&&gDisabledRestart) + { + tCarPhysics *phys=(tCarPhysics*)gCarEntities[gGameInfo->playerID]->physics; + phys->damage=0; + RoadCenterCar(gCarEntities[gGameInfo->playerID]); + gDisabledRestart=0; + } + int numDisabled=0; + for(int i=0;inumPlayers;i++) + { + if(gCarEntities[i]->id>=0) + { + tCarPhysics *phys=(tCarPhysics*)gCarEntities[i]->physics; + if(phys->damage>=kFatalDamage) + numDisabled++; + } + } + if(numDisabled>0&&numDisabled>=gGameInfo->numPlayers-1) + if(!gDisabledRestart) + gDisabledRestart=gFrameCount; + } + else + { + if(gFrameCount*kFrameTime>=3) + for(int i=0;inumPlayers;i++) + { + tCarPhysics *phys=(tCarPhysics*)gCarEntities[i]->physics; + if(!(gMapInfo->needsToStop&&(sqr(gCarEntities[i]->velo)>0.5)&&(phys->checkPoint==kNumCheckPoints-1))) + { + if(phys->lap<=gGameInfo->numLaps||(phys->checkPoint==0&&phys->lap==gGameInfo->numLaps+1)||gGameInfo->numLaps==-1) + { + //this doesn't all make sense but works for now. + if(gGameInfo->reverse){ + if(gCheckPoints[(phys->checkPoint+1)%kNumCheckPoints].index>gCheckPoints[phys->checkPoint].index){ + if(phys->positioncheckPoint].index||(phys->position>gCheckPoints[(phys->checkPoint+1)%kNumCheckPoints].index&&gMapInfo->loop)) + CheckPointPass(gCarEntities[i]); + }else if(phys->positioncheckPoint].index&&phys->position>gCheckPoints[(phys->checkPoint+1)%kNumCheckPoints].index) + CheckPointPass(gCarEntities[i]); + } + else{ + if(gCheckPoints[(phys->checkPoint+10)%kNumCheckPoints].indexcheckPoint].index){ + if(phys->position>gCheckPoints[phys->checkPoint].index) + CheckPointPass(gCarEntities[i]); + }else if(phys->position>gCheckPoints[phys->checkPoint].index&&phys->positioncheckPoint+10)%kNumCheckPoints].index) + CheckPointPass(gCarEntities[i]); + } + } + } + + for(int j=0;jnumPlayers;j++) + { + tCarPhysics *jPhys=(tCarPhysics*)gCarEntities[j]->physics; + if(((gGameInfo->reverse&&phys->positionposition)||(!gGameInfo->reverse&&phys->position>jPhys->position))&&phys->lap>jPhys->lap+phys->lappedPlayers[j]) + { + phys->lappedPlayers[j]++; + if(gCarEntities[i]==gViewedEntity&&!gReplay&&gGameInfo->netID[j]!=-1&&gCarEntities[j]->id!=-1) + TextPrintfToBufferFormatedFading(Vector(kFadingMessageXPos,kFadingMessageYPos),kFadingMessageSize,kTextAlignMiddle,2.0,3.0,"You lapped %s!!",gGameInfo->network?gGameInfo->playerNames[j]:jPhys->car.carName); + else if(gCarEntities[j]==gViewedEntity&&!gReplay) + TextPrintfToBufferFormatedFading(Vector(kFadingMessageXPos,kFadingMessageYPos),kFadingMessageSize,kTextAlignMiddle,2.0,3.0,"You were lapped by %s!!",gGameInfo->network?gGameInfo->playerNames[i]:phys->car.carName); + } + } + } + + + if(gGameInfo->numLaps==-1||gGameInfo->maxTime!=0) + TrackerWaitForLapTimeRegistry(); + + gAccelSignDisplayIntensity-=kFrameTime*0.5; + } +} + + +#define kMinSteerResistance 0.3 +#define kNormalSteerAlignment 0.65 + +#define kSteerResistanceFactor 0.4 +#define kSteerAlignmentFactor 1.5 + +//creates force-feedback effects when the the players car is sliding +void FFBResponse() +{ + tCarPhysics *phys=(tCarPhysics*)gViewedEntity->physics; + tCarDefinition *car=&(phys->car); + + float slideVelo=0; + for(int i=0;inumWheels;i++) + slideVelo+=gSurfaceTypes->types[phys->wheels[i].surfaceType].soundEnable?phys->wheels[i].slipVelo:0; + slideVelo/=car->numWheels; + slideVelo*=0.02; + if(slideVelo>0.5)slideVelo=0.5; + FFBiShockDirect(slideVelo,slideVelo); + + float velo=~gViewedEntity->velo; + float bumpHeight=(gSurfaceTypes->types[phys->wheels[0].surfaceType].bumpHeight+gSurfaceTypes->types[phys->wheels[1].surfaceType].bumpHeight)*0.5; + FFBSetGoundRumble(velo,bumpHeight*10); + + float wheelsSlipFactor=1-(fabs(phys->wheels[0].slipAngle)+fabs(phys->wheels[1].slipAngle))*5-(fabs(phys->wheels[0].slip)+fabs(phys->wheels[1].slip))*0.5; + if(wheelsSlipFactor<0)wheelsSlipFactor=0; + + int wheelsOnGround=0; + for(int i=0;i<2;i++) + if(phys->wheels[i].suspensionwheels[i].maxSuspension) + wheelsOnGround++; + + wheelsSlipFactor*=wheelsOnGround/2.0; + + float steerResistance=(1-kMinSteerResistance)*(1/(velo*0.2+1))+kMinSteerResistance; + float steerAlignment=velo<10?velo/10*kNormalSteerAlignment:kNormalSteerAlignment+((velo-10)/70)*(1-kNormalSteerAlignment); + + FFBSetSteerResistance(kSteerResistanceFactor*steerResistance*wheelsSlipFactor,kSteerAlignmentFactor*steerAlignment*wheelsSlipFactor); +} + + +tVector3 gCameraViewPos; + +void GetNewTripod(tGameEntity *cameraViewEntity) +{ + float side=RandomProb(0.5)?-1:1; + float speed; + gCameraEntity->pos=RoadGetNextWaypoint(cameraViewEntity->pos,&cameraViewEntity->lastRoadIndex,&side,&speed,((gCameraEntity->lastRoadIndexlastRoadIndex)^gGameInfo->reverse)?100:-100)+Vector(0,RandomFl(4,13),0); +} + + + +void CalcCamera() +{/* + gCameraEntity->pos=gViewedEntity->pos+Vector(0,2000,0); + MatrixIdentity(gCameraEntity->dir); + MatrixRotX(gCameraEntity->dir,PI/2); +return; +*/ + + tGameEntity *cameraViewEntity=gCarEntities[gReplayViewedEntityID]; + gCameraEntity->velo=cameraViewEntity->velo; + tVector3 veloFlat,posFlat; + tVector3 cameraPos=gCameraEntity->pos; + switch(gCameraMode) + { + case kCameraFree: + if(GetInterfaceKey(kInterfaceKeyLeft)) + MatrixRotY(gCameraEntity->dir,kFrameTime*3); + if(GetInterfaceKey(kInterfaceKeyRight)) + MatrixRotY(gCameraEntity->dir,-kFrameTime*3); + if(GetInterfaceKey(kInterfaceKeyUp)) + { + tMatrix3 m; + RotationVectorToMatrix(-kFrameTime*2**MatrixGetXVector(gCameraEntity->dir),m); + MatrixMult(gCameraEntity->dir,m,gCameraEntity->dir); + } + if(GetInterfaceKey(kInterfaceKeyDown)) + { + tMatrix3 m; + RotationVectorToMatrix(kFrameTime*2**MatrixGetXVector(gCameraEntity->dir),m); + MatrixMult(gCameraEntity->dir,m,gCameraEntity->dir); + } + if(GetInterfaceKey(kInterfaceKeySpace)&&!gInputChatMode) + cameraPos=cameraPos+*MatrixGetZVector(gCameraEntity->dir)*kFrameTime*25; + gCameraEntity->velo=Vector(0,0,0); + break; + case kCameraChase: + veloFlat=Vector(cameraViewEntity->velo.x,0,cameraViewEntity->velo.z); + posFlat=Vector(cameraViewEntity->pos.x,0,cameraViewEntity->pos.z); + cameraPos=posFlat-(sqr(veloFlat)dir):!veloFlat)*(gCameraReverse?-9:9); + cameraPos.y=cameraViewEntity->pos.y+2.7; + break; + case kCameraChaseClose: + veloFlat=Vector(cameraViewEntity->velo.x,0,cameraViewEntity->velo.z); + posFlat=Vector(cameraViewEntity->pos.x,0,cameraViewEntity->pos.z); + cameraPos=posFlat-(sqr(veloFlat)dir):!veloFlat)*(gCameraReverse?-6:6); + cameraPos.y=cameraViewEntity->pos.y+1.85; + break; + case kCameraLeftSide: + cameraPos=cameraViewEntity->pos-*MatrixGetXVector(cameraViewEntity->dir)*(gCameraReverse?-10:10); + cameraPos.y=cameraViewEntity->pos.y+2; + break; + case kCameraTripod: + if(sqr(gCameraEntity->pos-cameraViewEntity->pos)>sqr(120)) + GetNewTripod(cameraViewEntity); + cameraPos=gCameraEntity->pos; + gCameraEntity->velo=Vector(0,0,0); + break; + case kCameraTop: + { + float heigth=7+~cameraViewEntity->velo; + if(heigth>40) + heigth=40; + cameraPos=cameraViewEntity->pos+Vector(0,heigth,1); + } + break; + case kCameraCockpit: + case kCameraCockpitCarHidden: + { + tCarPhysics *phys=(tCarPhysics*)cameraViewEntity->physics; + tCarDefinition *car=&(phys->car); + cameraPos=cameraViewEntity->pos + +((gCameraMode==kCameraCockpit)?(*MatrixGetXVector(cameraViewEntity->dir)*car->driverPos.x):Vector(0,0,0)) + +*MatrixGetYVector(cameraViewEntity->dir)*(car->driverPos.y+0.5) + +*MatrixGetZVector(cameraViewEntity->dir)*car->driverPos.z; + } + break; + case kCameraLeftWheel: + cameraPos=cameraViewEntity->pos-*MatrixGetXVector(cameraViewEntity->dir)*(gCameraReverse?-1.2:1.2)+*MatrixGetYVector(cameraViewEntity->dir)*0.4+*MatrixGetZVector(cameraViewEntity->dir)*0.5; + break; + } + + + if(gFrameCount*kFrameTime<1.5&&!gReplay) + { + float f=(gFrameCount*kFrameTime-1.5)/3*2*PI; + cameraPos=cameraViewEntity->pos- + *MatrixGetXVector(cameraViewEntity->dir)*sin(f)*15+ + *MatrixGetYVector(cameraViewEntity->dir)*(2.0-20.0*f)- + *MatrixGetZVector(cameraViewEntity->dir)*cos(f)*15; + } + + tVector3 cameraViewPos=cameraPos-cameraViewEntity->pos; + if(sqr(gCameraViewPos-cameraViewPos)pos+cameraViewPos; +// if(sqr(gCameraEntity->pos-newPos)>25) +// gCameraEntity->lastRoadIndex=0; + gCameraEntity->pos=newPos; + cameraViewEntity=cameraViewEntity; + + if(gCameraMode==kCameraCockpit||gCameraMode==kCameraLeftWheel||gCameraMode==kCameraCockpitCarHidden&&!(gFrameCount*kFrameTime<1.5&&!gReplay)) + { + MemoryMove(gCameraEntity->dir,cameraViewEntity->dir,sizeof(tMatrix4)); + if((gCameraMode==kCameraCockpit||gCameraMode==kCameraCockpitCarHidden)&&gCameraReverse) + { + *MatrixGetXVector(gCameraEntity->dir)=-*MatrixGetXVector(gCameraEntity->dir); + *MatrixGetZVector(gCameraEntity->dir)=-*MatrixGetZVector(gCameraEntity->dir); + } + + } + else if(gCameraMode!=kCameraFree) + { + *MatrixGetZVector(gCameraEntity->dir)=!(cameraViewEntity->pos-gCameraEntity->pos+Vector(0,1.2,0)); + *MatrixGetYVector(gCameraEntity->dir)=Vector(0,1,0); + MatrixReAdjust(gCameraEntity->dir); + } + + if(gCameraMode!=kCameraFree) + { + tVector3 normal; + float dist=GetGroundOffset(gCameraEntity->pos,&gCameraEntity->lastRoadIndex,&normal,0); + if(dist<0) + if(!isinf(dist)) + gCameraEntity->pos=gCameraEntity->pos-normal*dist; + } +} + + +void MoveGhostCar() +{ + if(gGameInfo->numLaps!=-1) + return; + tCarPhysics *phys=(tCarPhysics*)gCarEntities[0]->physics; + if(phys->lapCount<2) + return; + + LogReplayGhostFrame(); +} + +//use this define to disable all game physics. +//useful to test raw graphics performance. +//#define __NOPHYSICS + + +//Calculate one game physics frame +void GameFrame() +{ +/*static int p=false; +if(Button()&&!p){gCarEntities[0]->pos=gCameraEntity->pos;p=true;}else if(!Button())p=false;*/ + if(!gPaused) + { + #ifndef __NOPHYSICS + //Move all entities + MotionFrame(); + + //check for collision between entities and ground or each other + CollisionFrame(); + + //check if cars have passed any checkpoint or lap start/finish line. + LapCheck(); + + //send local physics data to network + NetworkSendPhysics(); + #endif + } + + //get AI or user input for local cars + ControlFrame(); + + if(!gPaused) + { + #ifndef __NOPHYSICS + + //Calculate car sound effects + SoundFrame(); + + //calculate all local entities physics + PhysicsFrame(); + + //process particles + ParticlesProcess(); + } + //receive physics for remote entities + NetworkReceivePhysics(); + + if(!gPaused) + { + MoveGhostCar(); + + //create force-feedback effects when the the players car is sliding + FFBResponse(); + #endif + } + + CalcCamera(); + + //check if we have time to draw a graphics frame + if(CheckFrameTime()||gPaused) + { + //draw frame + RenderFrame(true); + + //if we are too fast, waste some time + CheckTimeSkip(); + + if(gLightningflashIntensity*kFrameTime)) + gLightning=gFrameCount; + + if(gFrameCount>gNetPauseTime&&gNetPauseTime!=0) + PauseGame(); + + if(gFrameCount%30) + SystemPoll(true); + +} + +int GameReplayFrame() +{ + if(!gPaused||!gBackgroundReplay) + { + CollisionFrame(); + LapCheck(); + SoundFrame(); + ParticlesProcess(); + MotionFrame(); + LogReplayFrame(); + if(!(gBackgroundReplay&&gInterfaceType)) + ControlFrame(); + PhysicsFrame(); + } + CalcCamera(); + + if(gGameInfo->numLaps==-1&&!gBackgroundReplay) + TrackerWaitForLapTimeRegistry(); + + int hasRendered=false; + //check if we have time to draw a graphics frame + if(CheckFrameTime()||gPaused) + { + //draw frame + RenderFrame(true); + + //if we are too fast, waste some time + CheckTimeSkip(); + hasRendered=true; + } + + if(gFrameCount%30&&!gBackgroundReplay) + SystemPoll(true); + + if(!gPaused||!gBackgroundReplay) + gFrameCount++; + return hasRendered; +} \ No newline at end of file diff --git a/source/gameframe.h b/source/gameframe.h new file mode 100755 index 0000000..0e03ccc --- /dev/null +++ b/source/gameframe.h @@ -0,0 +1,41 @@ +#ifndef __GAMEFRAME +#define __GAMEFRAME + +#define kFPS 75.0 +#define kFrameTime (1/kFPS) + +#define kStartGameDelaySeconds 5.0 +enum{ + kEndGame=1, + kEndGameNoLeave, + kEndGameNoReplay, + kEndGameRestart +}; + +extern int gFrameCount; +extern int gGraphFrameCount; +extern int gGameEnd,gRaceFinished,gDisqualified; +extern int gPaused; +extern int gNetPauseTime; +extern float gFPS; +extern float gStartTime; + +extern float gAccelSignDisplayIntensity,gAccelSignDisplayCorner; +extern float gTimeStretch; +extern int gCurrentLapStart; +extern int gBestLapStart; +extern int gLastLapTime; +extern int gBestLapTime; +extern int gWorldRecord,gLocalRecord; +extern int gDisabledRestart; +extern char gRecordName[]; + +#define kMinActivityVelo 1.2 + +void GameFrame(); +int GameReplayFrame(); +void StartFrameCount(); +void PauseGame(); +void UnPauseGame(); + +#endif \ No newline at end of file diff --git a/source/gameinitexit.cpp b/source/gameinitexit.cpp new file mode 100755 index 0000000..e9a5048 --- /dev/null +++ b/source/gameinitexit.cpp @@ -0,0 +1,772 @@ +//gameinitexit.cpp +//initialize a new game and dispose all the game structures after the game has ended + + +#include +#include +#include +#include "gametime.h" +#include "gamemem.h" +#include "entities.h" +#include "fileio.h" +#include "sky.h" +#include "carphysics.h" +#include "gameframe.h" +#include "roads.h" +#include "parser.h" +#include "network.h" +#include "environment.h" +#include "config.h" +#include "gameinitexit.h" +#include "error.h" +#include "gamesound.h" +#include "text.h" +#include "renderframe.h" +#include "controls.h" +#include "tracks.h" +#include "stencil.h" +#include "log.h" +#include "networkphysics.h" +#include "random.h" +#include "particles.h" +#include "interfaceutil.h" +#include "tracker.h" +#include "screen.h" +#include "textures.h" +#include "gamesystem.h" +#include "interface.h" +#include "interfacemultiplayer.h" +#include "initexit.h" + +/* +#if __option(profile) +#include +#endif +*/ + + +//the map info structure +tMapInfo *gMapInfo=NULL; + +//the game info structure +tGameInfo *gGameInfo=NULL; + +//pointers to the game entities of the cars participating in the game +tGameEntity *gCarEntities[kMaxPlayers]; + +tGameEntity *gGhostEntity; + +tMapEnv *gMapEnv=NULL; + +//the check points along the track (invisible, used only for lead/trail calculation) +tCheckPoint gCheckPoints[kNumCheckPoints]; + + +int gNumCornerSigns; +tCornerSign gCornerSigns[kMaxCornerSigns]; + +//is this a replay being watched? +int gReplay=false; +int gBackgroundReplay=false; +int gReplayAutoCam=false; +int gReplayViewedEntityID; +int gReplayNextCam; +int gReplayOldFrameCount; + +void InitGInfo(tGameInfo *gInfo) +{ + gInfo->inited=false; + gInfo->version=0; + gInfo->numPlayers=0; + gInfo->numNetPlayers=0; + gInfo->reverse=0; + gInfo->environment=kFileErr; + gInfo->numLaps=0; + gInfo->map=kFileErr; + gInfo->network=false; + gInfo->playerID=0; + gInfo->arcade=kGameModeSim; + gInfo->maxTime=0; + gInfo->unused1=false; + gInfo->unused2=false; + gInfo->carsOnSpeed=gConfig->carsOnSpeed; + gInfo->demolition=gConfig->demolition; + gInfo->playerInited[0]=true; + strcpy(gInfo->environmentName,""); + strcpy(gInfo->mapName,""); + for(int i=0;iplayerCars[i]=kFileErr; + gInfo->playerColors[i]=0; + gInfo->playerAddOns[i]=0; + gInfo->netID[i]=0; + gInfo->ping[i]=0; + gInfo->playerVersions[i]=0; + strcpy(gInfo->playerCarNames[i],""); + strcpy(gInfo->playerNames[i],""); + } +} + + +//load map describtion from the file refered to by ref. +void ProcessMapInfo() +{ + if(gMapInfo->reverse) + gGameInfo->reverse=!gGameInfo->reverse; + + //get road types data + gRoadTypes=(tRoadTypeList*)FileGetParsedDataPtr(gMapInfo->roadTypes,kParserTypeRoadTypeDesc,sizeof(tRoadTypeList)); + + //initialize game entities defined by map + for(int i=0;inumObjs;i++) + { + if(gMapInfo->obj[i].envFlags==0||(gMapInfo->obj[i].envFlags&gEnvironment->envFlags)) + { + //create a new entity + tGameEntity *entity; + entity=EntityNew(gFirstEntity); + entity->renderType=kRenderTypeModel; + + //get entity position and direction from map info + entity->pos=gMapInfo->obj[i].pos; + EulerAnglesToMatrix(gMapInfo->obj[i].dir*kDegreeRadians,entity->dir); + + //get extension of file describing entity + char *extension=FileGetExtension(gMapInfo->obj[i].model); + + entity->physics=MemoryAllocateBlock(sizeof(int)); + *((int*)entity->physics)=gMapInfo->obj[i].color; + + entity->untouchable=gMapInfo->obj[i].untouchable; + + if(extension) + //is this entity just a model (no interaction with the game, just graphics). + if(!_stricmp(extension,kFileTypeModel)) + entity->renderData=gMapInfo->obj[i].model; + + //or is it a solid entity? + else if(!_stricmp(extension,kFileTypeSolidEntity)) + { + //initialize entity + entity->physicsType=kPhysicsTypeSolid; + entity->physicsData=gMapInfo->obj[i].model; + entity->physicsMachine=kPhysicsLocal; + + tSolidEntityPhysics *ent=(tSolidEntityPhysics*)FileGetParsedDataPtr(entity->physicsData,kParserTypeSolidEntityDesc,sizeof(tSolidEntityPhysics)); + if(ent->randomColor) + *((int*)entity->physics)=RandomInt(0,ent->numColors); + + entity->renderData=ent->model; + if(ent->movable) + entity->lastActivity=-1000; + } + else if(!_stricmp(extension,kFileTypeCarDefinition)) + { + //initialize entity + entity->renderType=kRenderTypeCar; + tCarDefinition *car=(tCarDefinition*)FileGetParsedDataPtr(gMapInfo->obj[i].model,kParserTypeCarDesc,sizeof(tCarDefinition)); + entity->renderData=car->model; + entity->physicsType=kPhysicsTypeCar; + entity->controlType=kControlTypeNone; + entity->physicsData=gMapInfo->obj[i].model; + entity->physics=MemoryAllocateZeroedBlock(sizeof(tCarPhysics)); + entity->physicsMachine=gGameInfo->network?(i==gGameInfo->playerID?kPhysicsLocal:kPhysicsRemote):kPhysicsLocal; + entity->lastActivity=-1000; + tCarPhysics *phys=(tCarPhysics*)entity->physics; + phys->car=*car; + phys->car.wheels=(tWheelDefinition*)MemoryAllocateBlock(sizeof(tWheelDefinition)*car->numWheels); + MemoryMove(phys->car.wheels,car->wheels,sizeof(tWheelDefinition)*car->numWheels); + phys->color=gMapInfo->obj[i].color; + } + + //is it something we don't understand? + else + { + char error[256]; + sprintf(error,"Illegal File Type %s Specified for Object",extension); + FailWithErrorString(error); + } + } + //else PrintConsoleString("%s: %d/%d",FileGetName(gMapInfo->obj[i].model),gMapInfo->obj[i].envFlags,gEnvironment->envFlags); + } + + gMapEnv=NULL; + for(int i=0;inumMapEnvs;i++) + if(gMapInfo->mapEnv[i].envFlags&gEnvironment->envFlags) + gMapEnv=gMapInfo->mapEnv+i; + + if(gMapEnv) + if(gMapEnv->fogBegin) + { + GLfloat fogColor[4]; + *(tVector3*)fogColor=gMapEnv->fogColor; + fogColor[3]=1; + glFogfv(GL_FOG_COLOR,fogColor); + glFogf(GL_FOG_START,gMapEnv->fogBegin); + glFogf(GL_FOG_END,gMapEnv->fogEnd); + } + //calculate road checkpoints + if(gMapInfo->loop) + RoadInitCheckPoints(gMapInfo->startPos); + else + { + RoadInitCheckPoints(gMapInfo->startPos,gMapInfo->finishPos); + gGameInfo->numLaps=1; + } + RoadInitCornerSigns(); +} + +void CalcCamera(); + +void InitCars() +{ + for(int i=0;inumPlayers;i++) + { + //get car describtion + tCarDefinition *car=(tCarDefinition*)FileGetParsedDataPtr(gGameInfo->playerCars[i],kParserTypeCarDesc,sizeof(tCarDefinition)); + + //initialize car entity + tGameEntity *entity; + entity=EntityNew(gFirstEntity); + entity->renderType=kRenderTypeCar; + entity->renderData=car->model; + entity->physicsType=kPhysicsTypeCar; + entity->controlType=(i==gGameInfo->playerID?kControlTypeUserInput:kControlTypeAIInput); + //entity->controlType=kControlTypeAIInput; + entity->physicsData=gGameInfo->playerCars[i]; + entity->physics=MemoryAllocateZeroedBlock(sizeof(tCarPhysics)); + entity->physicsMachine=gGameInfo->network?(i==gGameInfo->playerID?kPhysicsLocal:kPhysicsRemote):kPhysicsLocal; + if(i>=gGameInfo->numNetPlayers&&gGameInfo->playerID==0) + entity->physicsMachine=kPhysicsLocal; + gCarEntities[i]=entity; + + //position car at start position + int startAtEnd=(gGameInfo->reverse&&!gMapInfo->loop); + tVector3 startPos=startAtEnd?gMapInfo->finishPos:gMapInfo->startPos; + + if(gGameInfo->numLaps==-1&&gMapInfo->loop) + { + int roadIndex=0; + float side=0,speed; + startPos=RoadGetNextWaypoint(gMapInfo->startPos,&roadIndex,&side,&speed,-700); + + } + tVector3 startDir=RoadGetDir(RoadGetPosition(startPos,0,NULL)); + + entity->pos=startPos-startDir*((i/2)*10+gMapInfo->startLineOffset+(i%2?gMapInfo->carOffset:0))+!(startDir%Vector(0,1,0))*(i%2?-1:1)*gMapInfo->startCenterOffset; + entity->netPos=entity->pos; + *MatrixGetZVector(entity->dir)=startDir; + *MatrixGetYVector(entity->dir)=Vector(0,1,0); + MatrixReAdjust(entity->dir); + + tCarPhysics *phys=(tCarPhysics*)entity->physics; + + if(i==gGameInfo->playerID) + gViewedEntity=entity; + + //set up car specs + if(startAtEnd) + phys->checkPoint=kNumCheckPoints-1; + phys->car=*car; + phys->car.wheels=(tWheelDefinition*)MemoryAllocateBlock(sizeof(tWheelDefinition)*car->numWheels); + MemoryMove(phys->car.wheels,car->wheels,sizeof(tWheelDefinition)*car->numWheels); + phys->addOns=gGameInfo->playerAddOns[i]; + phys->color=gGameInfo->playerColors[i]; + phys->aiPowerCycle=RandomFl(0,2*PI); + phys->aiRouteCycle=RandomFl(0,2*PI); + phys->position=RoadGetPosition(entity->pos,entity->lastRoadIndex,NULL); + if(i>0) + { + tCarPhysics *phys2=(tCarPhysics*)gCarEntities[0]->physics; + if(phys->position>phys2->position&&!gGameInfo->reverse) + phys->lap=-1; + else if(phys->positionposition&&gGameInfo->reverse) + phys->lap=-1; + } + + if(gGameInfo->playerNames[i][0]) + phys->plateName=gGameInfo->playerNames[i]; + + for(int j=0;j<5;j++) + phys->dirtStretch[j]=RandomFl(-0.5,0.5); + InstallCarAddOns(phys); + + //if car is on left side, prepare for overtaking + //(otherwise ai players will immediatly try to change lanes, not a good idea). + if(i%2) + { + phys->overtaking=gCarEntities[0]; + phys->overtakeSide=-1; + } + + } +} + +void InitGameEntities() +{ + //initialize entity list + EntityResetCount(); + gFirstEntity=(tGameEntity*)MemoryAllocateZeroedBlock(sizeof(tGameEntity)); + gFirstEntity->next=gFirstEntity; + gFirstEntity->prev=gFirstEntity; + + //process map info describtion + ProcessMapInfo(); + + //initialize car entities + InitCars(); + + //initialize camera + gCameraEntity=EntityNew(gFirstEntity); + + //initialize ghost + if(gGameInfo->numLaps==-1) + { + gGhostEntity=EntityNew(gFirstEntity); + gGhostEntity->physics=MemoryAllocateZeroedBlock(sizeof(tCarPhysics)); + MemoryMove(gGhostEntity->physics,gCarEntities[0]->physics,sizeof(tCarPhysics)); + gGhostEntity->pos=Vector(10000,10000,10000); + gGhostEntity->physicsType=kPhysicsTypeGhost; + gGhostEntity->renderType=kRenderTypeGhost; + gGhostEntity->controlType=kControlTypeNone; + gGhostEntity->physicsData=gGameInfo->playerCars[0]; + gGhostEntity->renderData=gCarEntities[0]->renderData; + } +} + +void InitInfoDisplay(); + +//initialize a new game +int InitGame() +{ + if(gFileTampered) + Exit(); + + //get map info data + gMapInfo=(tMapInfo*)FileGetParsedDataPtr(gGameInfo->map,kParserTypeMapInfoDesc,sizeof(tMapInfo)); + + //load enivronment describtion + LoadEnvironment(gGameInfo->environment); + + //initialize fog + GLfloat fogColor[4]; + *(tVector3*)fogColor=gEnvironment->fogColor; + fogColor[3]=1; + glFogfv(GL_FOG_COLOR,fogColor); + glFogf(GL_FOG_START,600); + glFogf(GL_FOG_END,800); + + InitGameEntities(); + + //initialize game end flags + gInterfaceType=false; + gGameEnd=false; + gPaused=false; + gRaceFinished=false; + gDisqualified=false; + gLightning=false; + gInputEscMode=false; + gReplayViewedEntityID=gGameInfo->playerID; + gCameraMode=gConfig->cameraMode; + gCameraReverse=0; + gOutboxPos=0; + gGameChatMessage[0]='\0'; + gGameChatMessageSize=0; + gNetPauseTime=0; + + SoundSilence(); + + //reset replay log + LogReset(); + GhostLogReset(); + + TracksClear(); + ParticlesClear(); + + InitInfoDisplay(); + + //call once to reset stencil buffer + RenderStencilLayer(true,gConfig->stencil); + + //initialze lighting + SetupLighting(); + + //render a frame to get graphics loaded + CalcCamera(); + + gClipEnable=false; + float time=TimeGetSeconds(); + + gNumGameChatMessages=0; + gEnableTextureLoad=false; + gNumTexturesRequested=0; + RenderFrame(false); + gNumTexturesLoaded=0; + gEnableTextureLoad=true; + gDisabledRestart=0; + + for(int i=0;i0) + InterfaceDrawStatusBar("Loading Textures...","Please Wait",gNumTexturesLoaded/(float)gNumTexturesRequested); + gTexturesQualityModifier=gFileTable[i].tagged+1; + TexturesSelectTex(i); + SystemPoll(true); + } + } + gTexturesQualityModifier=0; +// PrintConsoleString("loading took: %f seconds.",TimeGetSeconds()-time); + gClipEnable=true; + + //synchronize players on network + if(gGameInfo->network) + { + InterfaceDrawStrings("Waiting for other Players...","",kFileErr); + if(!NetworkSynch(gGameInfo->numNetPlayers)) + return false; + } + + //start clock + gTimeStretch=1.0; + gBestLapTime=0; + StartFrameCount(); + gPaused=false; + + return true; +} + +void DisposeEntities() +{ + if(gFirstEntity) + { + tGameEntity *entity=(tGameEntity*)gFirstEntity->next; + while(entity!=gFirstEntity) + { + tGameEntity *next=(tGameEntity*)entity->next; + if(entity->physics) + MemoryFreeBlock(entity->physics); + SoundFreeEntitySources(entity); + //FREE CAR PHYSICS! + MemoryFreeBlock(entity); + entity=next; + } + MemoryFreeBlock(gFirstEntity); + + gFirstEntity=NULL; + gViewedEntity=NULL; + gCameraEntity=NULL; + } +} + +void StartReplay() +{ + SoundSilence(); + tCarPhysics physStore[kMaxPlayers]; + +// for(int i=0;inumPlayers;i++) +// physStore[i]=*(tCarPhysics*)gCarEntities[i]->physics; + + DisposeEntities(); + + InitGameEntities(); + +/* for(int i=0;inumPlayers;i++) + { + *(tCarPhysics*)gCarEntities[i]->physics=physStore[i]; + tCarPhysics *phys=(tCarPhysics*)gCarEntities[i]->physics; + phys->dirt=0; + for(int j=0;jwheels[j].lastTrack=0; + }*/ + +// for(int i=0;ipos=Vector(10000,10000,10000); + if(gCameraMode==kCameraFree) + gCameraMode=kCameraTripod; + if(gGameInfo->numLaps==-1) + gGhostEntity->pos=Vector(10000,10000,10000); + + gFrameCount=gGameInfo->numLaps==-1?(gBestLapTime!=0?gBestLapStart+5:gCurrentLapStart+5):0; + gGraphFrameCount=gFrameCount; + gStartTime=TimeGetSeconds()-gFrameCount*kFrameTime; + gReplayIndex=0; + gReplayNextCam=gFrameCount+RandomFl(4,12)*kFPS; + + TracksClear(); +} +extern int gIndexPos; +extern int gGhostIndexPos; + +void ReplayProcessNetwork(int *end) +{ + if(gGameInfo->network) + { + tNetworkPacket message; + while(NetworkReceivePacket(&message)) + switch(message.what) + { + case kMessageTypeEndReplay: + *end=true; + gGameEnd=kEndGameNoLeave; + break; + case kMessageTypeGameTerminated: + *end=true; + return; + case kMessageTypeChat: + InsertGameChatMessage(((tChatMessage*)message.data)->str); + ChatBufferInsert(((tChatMessage*)message.data),gChatBuffer); + PlayInterfaceSound(FileGetReference("chat.wav")); + break; + case kMessageTypeBye: + { + if(message.from==0) + sprintf(gDisconnectString,"%s quit and stopped hosting.",gGameInfo->playerNames[0]); + int netID=message.from; + int id=-1; + //find the players id + for(int i=0;inumNetPlayers;i++) + if(gGameInfo->netID[i]==netID) + id=i; + + if(id!=-1) + { + tChatMessage m; + m.flags=kChatFlagSystem; + sprintf(m.str,"### %s is quitting.",gGameInfo->playerNames[id]); + SoundReInit(); + PlayInterfaceSound(FileGetReference("chat.wav")); + InsertGameChatMessage(m.str); + ChatBufferInsert(&m,gChatBuffer); + } + } + break; + case kMessageTypePlayerLeft: + { + int netID=message.from; + int id=-1; + //find the players id + for(int i=0;inumPlayers;i++) + if(gGameInfo->netID[i]==netID) + id=i; + if(id!=-1) + { + char leftMessage[256]; + //PrintConsoleString("### Player %d left the game. netID: %d",id,netID); + sprintf(leftMessage,"### %s left the game.",gGameInfo->playerNames[id]); + InsertGameChatMessage(leftMessage); + //KillPlayer(id); + gGameInfo->netID[id]=-1; + } + } + break; + } + } +} + + +int ReplayFrame() +{ + int end=false; + static int save=false; + if(GetInterfaceKey(kInterfaceKeyEsc)&&!gGameInfo->network) + end=true; + if(GetInterfaceKey(kInterfaceKeyCmd)&&GetInterfaceKey(kInterfaceKeyOpt)&&GetInterfaceKey(kInterfaceKeyS)&&!gBackgroundReplay) + { + if(!save) + { + LogSaveToDisk(); + save=true; + } + } + else save=false; + if(gGameEnd) + end=true; + ReplayProcessNetwork(&end); + if(gFrameCount>=gReplayOldFrameCount||gReplayIndex>=(gGameInfo->numLaps==-1?gGhostIndexPos:gIndexPos)) + { + if(gMapInfo->reverse) + gGameInfo->reverse=!gGameInfo->reverse; + StartReplay(); + } + if(gFrameCount>=gReplayNextCam&&gReplayAutoCam) + { + ParticlesClearScreen(); + gCameraReverse=RandomProb(0.4); + gCameraMode=RandomProb(0.3)?kCameraTripod:RandomInt(kCameraChase,kCameraTop+1); + int nextViewID; + do{ + nextViewID=RandomInt(0,gGameInfo->numPlayers); + }while(gGameInfo->netID[nextViewID]==-1); + tGameEntity *nextView=gCarEntities[nextViewID]; + if(sqr(nextView->velo)>=1.0) + { + gViewedEntity=nextView; + gReplayViewedEntityID=nextViewID; + } + float cameraDuration; + switch(gCameraMode) + { + case kCameraChase: + case kCameraChaseClose: + cameraDuration=RandomFl(3,10);break; + case kCameraLeftSide: + case kCameraLeftWheel: + cameraDuration=RandomFl(2,5);break; + case kCameraTop: + cameraDuration=RandomFl(3,6);break; + case kCameraTripod: + cameraDuration=RandomFl(8,18);break; + case kCameraCockpit: + case kCameraCockpitCarHidden: + cameraDuration=RandomFl(2,5); + gCameraReverse=false; + break; + } + gReplayNextCam=gReplayNextCam+cameraDuration*kFPS; + } + + while(!GameReplayFrame()&&gBackgroundReplay); + + return end; +} + +void RunReplay() +{ + TextClearBufferFading(); +//LogLoad(FileGetReference("test.log"),gGameInfo); + gReplay=true; + if(gGameInfo->numLaps==-1) + { + tCarPhysics *phys=(tCarPhysics*)gCarEntities[0]->physics; + if(phys->lapCount<=1) + LogToGhostLog(); + } + gReplayOldFrameCount=(gGameInfo->numLaps==-1&&gBestLapTime!=0)?gBestLapTime+gBestLapStart:gFrameCount; + if(gReplayOldFrameCount<10)return; + LogSort(); + //LogSaveToDisk(); + gReplayAutoCam=true; + + StartReplay(); + while(!ReplayFrame()); + + if(gGameInfo->network&&gGameInfo->playerID==0) + { + NetworkSendPacket(kMessageTypeEndReplay,NULL,0,kMessagePriorityHigh,kMessageSendToAllButSelf); + } +// gReplay=false; +} + +void ExitGame() +{ +/* #ifndef __APPLE_CC__ + #if __option(profile) + ProfilerDump("\pProfiler Dump"); + #endif + #endif +*/ + FFBStop(); + UnPauseGame(); + if(gGameInfo->network) + { + if(gGameInfo->playerID==0) + NetworkSendPacket(kMessageTypeEndGame,NULL,0,kMessagePriorityHigh,kMessageSendToAllButSelf); + else if(gGameEnd!=kEndGameNoLeave) + { + NetworkSendPacket(kMessageTypeBye,NULL,0,kMessagePriorityHigh,kMessageSendToAllButSelf); + NetworkSendPacket(kMessageTypeGameTerminated,NULL,0,kMessagePriorityHigh,gGameInfo->netID[gGameInfo->playerID]); + } + } + if((gGameEnd==kEndGameNoLeave||(gGameEnd==kEndGame&&!(gGameInfo->network&&gGameInfo->playerID!=0)))&&!gGameInfo->demolition) + { + gGameEnd=false; + while((GetInterfaceKey(kInterfaceKeyReturn)||GetInterfaceKey(kInterfaceKeyEnter)||GetInterfaceKey(kInterfaceKeyEsc))); + RunReplay(); + if(gGameInfo->network) + { + if(gGameInfo->playerID==0) + NetworkSendPacket(kMessageTypeEndGame,NULL,0,kMessagePriorityHigh,kMessageSendToAllButSelf); + else if(gGameEnd!=kEndGameNoLeave) + { + NetworkSendPacket(kMessageTypeBye,NULL,0,kMessagePriorityHigh,kMessageSendToAllButSelf); + NetworkSendPacket(kMessageTypeGameTerminated,NULL,0,kMessagePriorityHigh,gGameInfo->netID[gGameInfo->playerID]); + } + } + } + +/* if(gGameEnd!=kEndGameRestart) + { + float startTime=TimeGetSeconds(); + float t; + do{ + t=TimeGetSeconds()-startTime; + RenderFrame(false); + InterfaceDrawBackgroundFade(t/0.3,true); + ScreenBlit(); + }while(t<0.3); + }*/ + + DisposeEntities(); + + gInputChatMode=false; + gFrameCount=0x7fffffff; + TextClearBuffer(); + TracksClear(); + SoundSilence(); + ParticlesClear(); + glClear(GL_COLOR_BUFFER_BIT); + gMapInfo=NULL; + gGameInfo=NULL; + gMapEnv=NULL; + TrackerLapTimeRegistryClose(); + LoadEnvironment(FileGetReference("showroom.senv")); + FlushKeys(); +} + +int GetPlayerRanking() +{ + tCarPhysics *phys=(tCarPhysics*)gCarEntities[gGameInfo->playerID]->physics; + if(phys->lapCount>gGameInfo->numLaps&&!gDisqualified) + { + int place=1; + for(int i=0;inumPlayers;i++) + { + tCarPhysics *phys2=(tCarPhysics*)gCarEntities[i]->physics; + if(phys2->lapCount>gGameInfo->numLaps) + if(phys2->lapTimes[gGameInfo->numLaps]lapTimes[gGameInfo->numLaps]) + place++; + } + if(gGameInfo->maxTime) + return phys->lapTimes[gGameInfo->numLaps]; + if(gGameInfo->numLaps==-1) + return gBestLapTime; + return place; + } + else + return -1; +} + +int RunGame(tGameInfo *gInfo) +{ + gReplay=false; + int place; + int reverse=gInfo->reverse; + do{ + gGameInfo=gInfo; + if(InitGame()) + while(!gGameEnd) + GameFrame(); + place=GetPlayerRanking(); + gInfo->reverse=reverse; + ExitGame(); + gInfo->reverse=reverse; + }while(gGameEnd==kEndGameRestart); + gInterfaceGInfo=*gInfo; + StartBackgroundReplay(); + return place; +} \ No newline at end of file diff --git a/source/gameinitexit.h b/source/gameinitexit.h new file mode 100644 index 0000000..8199e7e --- /dev/null +++ b/source/gameinitexit.h @@ -0,0 +1,179 @@ +#ifndef __GAMEINITEXIT +#define __GAMEINITEXIT + +#include "vectors.h" +#include "entities.h" +#include "fileio.h" + +#define kMaxPlayers 12 +#define kMaxLaps 100 +#define kNumCheckPoints 64 +#define kMaxCornerSigns 64 + +typedef struct{ + tFileRef model; + int color; + int envFlags; + int untouchable; + tVector3 pos,dir; +}tMapObjectDef; + +typedef struct{ + float time; + tGameEntity *setBy; + int hit; +}tPassTimeRec; + +typedef struct{ + float index; + tPassTimeRec passTimes[kMaxLaps+2]; +}tCheckPoint; + +typedef struct{ + float pos; + float accel; +}tCornerSign; + +typedef struct{ + tVector3 a,b,c; +}tVisWall; + +typedef struct{ + int envFlags; + int lightEnable; + float fogBegin,fogEnd; + float fogOscillation,fogOscillationSpeed; + tVector3 fogColor; + tVector3 lightDir,flareDir; + tFileRef sky0,sky90,sky180,sky270,skytop,skybot; + tFileRef spheremap; + tFileRef lightMap; + tVector2 lightMapTopLeft,lightMapBotRight,lightMapSpeed; + tFileRef lightMap2; + tVector2 lightMap2TopLeft,lightMap2BotRight,lightMap2Speed; +}tMapEnv; + +typedef struct{ + tFileRef road; + tFileRef image; + int loop,dirtEnable; + int hideMap; + int dontDrawRoad; + int builtIn; + int demoAvailable; + int needsToStop; + int useAltEnv; + tFileRef dirtMap; + int maxPlayers; + int hasOverview; + tFileRef overview; + tVector2 overviewTopLeft,overviewBotRight; + + char name[256]; + int playerPos; + int reverse; + float startLineOffset,startCenterOffset,carOffset; + float speedFactor; + float dirtIntensity; + tVector3 startPos; + tVector3 finishPos; + tFileRef roadTypes; + int numObjs; + tMapObjectDef *obj; + int numVisWalls; + int baseSurfaceType; + tVisWall *visWalls; + int numMapEnvs; + tMapEnv* mapEnv; +}tMapInfo; + +enum { + kGameInfoNetworkEnable=1<<0, + kGameInfoNetworkForcePlayerCar=1<<1, + kGameInfoNetworkCantGoBackwards=1<<2 +}; + +#define kMaxNameLength 64 + +enum { + kGameModeSim=0, + kGameModeArcade, + kGameModeTurbo, + kGameModeStrict +}; + +typedef struct{ + UInt8 inited; + UInt8 numNetPlayers; + UInt8 numPlayers; + UInt8 reverse; + SInt8 numLaps; + UInt8 network; + UInt8 playerID; + SInt8 arcade; + UInt8 carsOnSpeed; + UInt8 demolition; + UInt8 unused1; + UInt8 unused2; + float maxTime,silverTime,goldTime; + UInt32 version; + tFileRef map; + tFileRef environment; + char environmentName[kMaxFileNameLength]; + char mapName[kMaxFileNameLength]; + UInt8 playerColors[kMaxPlayers]; + SInt8 netID[kMaxPlayers]; + tFileRef playerCars[kMaxPlayers]; + int playerAddOns[kMaxPlayers]; + float ping[kMaxPlayers]; + UInt8 playerInited[kMaxPlayers]; + int playerVersions[kMaxPlayers]; + char playerCarNames[kMaxPlayers][kMaxFileNameLength]; + char playerNames[kMaxPlayers][kMaxNameLength]; +}tGameInfo; + +typedef struct{ + UInt8 inited; + UInt8 numNetPlayers; + UInt8 numPlayers; + UInt8 reverse; + SInt8 numLaps; + UInt8 network; + SInt8 arcade; + UInt8 carsOnSpeed; + UInt8 demolition; + UInt32 version; + char environmentName[kMaxFileNameLength]; + char mapName[kMaxFileNameLength]; + UInt8 playerColors[kMaxPlayers]; + SInt8 netID[kMaxPlayers]; + int playerAddOns[kMaxPlayers]; + float ping[kMaxPlayers]; + UInt8 playerInited[kMaxPlayers]; + int playerVersions[kMaxPlayers]; + char playerCarNames[kMaxPlayers][kMaxFileNameLength]; + char playerNames[kMaxPlayers][kMaxNameLength]; +}tNetGameInfo; + +extern tMapInfo *gMapInfo; +extern tMapEnv *gMapEnv; +extern tGameInfo *gGameInfo; +extern int gReplay; +extern int gBackgroundReplay; +extern int gReplayAutoCam; +extern int gReplayViewedEntityID; +extern tGameEntity *gCarEntities[kMaxPlayers]; +extern tGameEntity *gGhostEntity; +extern tCheckPoint gCheckPoints[kNumCheckPoints]; +extern int gNumCornerSigns; +extern tCornerSign gCornerSigns[kMaxCornerSigns]; +extern int gReplayOldFrameCount; + +void InitGInfo(tGameInfo *gInfo); +int RunGame(tGameInfo *gInfo); + +void StartReplay(); +void RunReplay(); +int ReplayFrame(); + +#endif \ No newline at end of file diff --git a/source/gamemem.h b/source/gamemem.h new file mode 100755 index 0000000..2aa13d2 --- /dev/null +++ b/source/gamemem.h @@ -0,0 +1,13 @@ +#ifndef __GAMEMEM +#define __GAMEMEM + +#define MemoryAllocateBlock(size) ((void*)NewPtr(size)) +#define MemoryResizeBlock(ptr,size) (SetPtrSize((Ptr)(ptr),size)) +#define MemoryFreeBlock(ptr) DisposePtr((Ptr)(ptr)) + +#define MemoryAllocateZeroedBlock(size) ((void*)NewPtrClear(size)) //allocated block of zeros +#define MemoryBlockSize(ptr) GetPtrSize((Ptr)(ptr)) //size of an allocated block + +#define MemoryMove(dest,source,n) BlockMoveData(source,dest,n) + +#endif \ No newline at end of file diff --git a/source/gamesound.h b/source/gamesound.h new file mode 100644 index 0000000..da74b3e --- /dev/null +++ b/source/gamesound.h @@ -0,0 +1,18 @@ +#ifndef __GAMESOUND +#define __GAMESOUND + +#include "fileio.h" +#include "entities.h" + +void SoundPause(); +void SoundInit(); +void SoundReInit(); +void SoundFrame(); +void SoundSilence(); +void SoundFreeEntitySources(tGameEntity *entity); + +void LoadAllSounds(); +void CarPlayCrashNoise(tGameEntity *carEntity,tFileRef sound,float volume); +void PlayInterfaceSound(tFileRef sound); + +#endif \ No newline at end of file diff --git a/source/gamesystem.h b/source/gamesystem.h new file mode 100644 index 0000000..a135abf --- /dev/null +++ b/source/gamesystem.h @@ -0,0 +1,22 @@ +#ifndef __SYSTEM +#define __SYSTEM + +extern int gSystemSuspended; +extern float giTunesLastStatusUpdate; +extern int giTunesPlaying; +extern char giTunesSong[1024],giTunesArtist[1024]; + +enum{ + kITunesPaused=0, + kITunesPlaying, + kITunesStopped, +}; + +void SystemInit(); +void SystemExit(); +void SystemPoll(int inGame); +void SystemYieldTime(float till); +void SystemNotify(); +int AutoUpdateRedline(char *updateURL,char* failStr); + +#endif \ No newline at end of file diff --git a/source/gametime.h b/source/gametime.h new file mode 100644 index 0000000..884703c --- /dev/null +++ b/source/gametime.h @@ -0,0 +1,9 @@ +#ifndef __GAMETIME +#define __GAMETIME + +#include + +//#define TimeGetSeconds() (clock()/(float)CLOCKS_PER_SEC) +float TimeGetSeconds(); + +#endif \ No newline at end of file diff --git a/source/infodisplay.cpp b/source/infodisplay.cpp new file mode 100644 index 0000000..f94956f --- /dev/null +++ b/source/infodisplay.cpp @@ -0,0 +1,1242 @@ +#include +#include +#include "text.h" +#include "entities.h" +#include "config.h" +#include "carphysics.h" +#include "gameframe.h" +#include "gamesystem.h" +#include "interfaceutil.h" +#include "rendercar.h" +#include "renderframe.h" +#include "environment.h" +#include "textures.h" +#include "controls.h" +#include "networkphysics.h" +#include "screen.h" +#include "gametime.h" +#include "network.h" +#include "tracker.h" + +#define kOverviewSize 0.22 +#define kDotSize 0.01 + +#define kWhiteDotFile "whitedot.pct" +#define kWhiteDiamondFile "whitediamond.pct" + +#define kCornerSignSize 0.1 +#define kCornerSignYPos 0.28 + +void RenderCornerSign() +{ + gTexturesQualityModifier=-255; + float corner=(fabs(gAccelSignDisplayCorner)-3)/5; + if(corner<0)corner=0; + if(corner>1)corner=1; + + float r=0.5+0.5*corner; + float g=1.0-corner; + float b=0; + float a=1; + if(gAccelSignDisplayIntensity>1.3) + a=1-(gAccelSignDisplayIntensity-1.3)/0.2; + else if(gAccelSignDisplayIntensity<0.8) + a=gAccelSignDisplayIntensity/0.8; + + if(!gConfig->guideSigns) + a=0; + + TexturesSelectTex(FileGetReference("cornersign.pct")); + + tCarPhysics *phys=(tCarPhysics*)gViewedEntity->physics; + if(phys->wrongDriectionFrames*kFrameTime>2) + { + a=(phys->wrongDriectionFrames*kFrameTime-2)*0.3>1?1:(phys->wrongDriectionFrames*kFrameTime-2)*0.3; + r=1; + g=0; + TexturesSelectTex(FileGetReference("wrongdirsign.pct")); + } + if(gFrameCount-phys->crashTime>kFPS*1) + if(sqr(gViewedEntity->velo)collision&&sqr(gViewedEntity->velo)crashTime)*kFrameTime-1)*0.5>1?1:((gFrameCount-phys->crashTime)*kFrameTime-1)*0.5; + r=1; + g=0.5; + TexturesSelectTex(FileGetReference("towsign.pct")); + } + + if(a==0) + return; + + glPushMatrix(); + + glPushAttrib(GL_DEPTH_BUFFER_BIT+GL_LIGHTING_BIT+GL_COLOR_BUFFER_BIT+GL_TEXTURE_BIT+GL_CURRENT_BIT); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + + glLoadIdentity(); + glTranslatef(0.0f,0.0f,-1.0f); + glTranslatef(0,kCornerSignYPos,0); + glScalef(kCornerSignSize*0.5,kCornerSignSize*0.5,0); + + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP); + + glColor4f(r,g,b,a*(1-gConfig->hudTransparency)); + glBegin(GL_TRIANGLE_STRIP); + if(gAccelSignDisplayCorner<0) + { + glTexCoord2d(1,1); glVertex2f(1,-1); + glTexCoord2d(1,0); glVertex2f(1,1); + glTexCoord2d(0,1); glVertex2f(-1,-1); + glTexCoord2d(0,0); glVertex2f(-1,1); + } + else + { + glTexCoord2d(0,1); glVertex2f(1,-1); + glTexCoord2d(0,0); glVertex2f(1,1); + glTexCoord2d(1,1); glVertex2f(-1,-1); + glTexCoord2d(1,0); glVertex2f(-1,1); + } + glEnd(); + + glPopAttrib(); + glPopMatrix(); + gTexturesQualityModifier=0; +} +#include "random.h" +void RenderOverview(float opacity,float x,float y) +{ + if(opacity==0) + return; + if(!gMapInfo->hasOverview) + return; + glPushMatrix(); + gTexturesQualityModifier=-255; + + glPushAttrib(GL_DEPTH_BUFFER_BIT+GL_LIGHTING_BIT+GL_COLOR_BUFFER_BIT+GL_TEXTURE_BIT+GL_CURRENT_BIT); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + + float ratio=(gMapInfo->overviewBotRight.y-gMapInfo->overviewTopLeft.y)/(gMapInfo->overviewBotRight.x-gMapInfo->overviewTopLeft.x); + glColor4f(gEnvironment->instrumentColor.x,gEnvironment->instrumentColor.y,gEnvironment->instrumentColor.z,(1-gConfig->hudTransparency)*opacity); + + glLoadIdentity(); + glTranslatef(0.0f,0.0f,-1.0f); + glTranslatef(x,y,0); + glScalef(kOverviewSize*0.5,kOverviewSize*ratio*0.5,0); + + TexturesSelectTex(gMapInfo->overview); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex2f(1,-2); + glTexCoord2d(1,0); glVertex2f(1,0); + glTexCoord2d(0,1); glVertex2f(-1,-2); + glTexCoord2d(0,0); glVertex2f(-1,0); + glEnd(); + + TexturesSelectTex(FileGetReference(kWhiteDotFile)); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP); + + + for(int i=0;inumPlayers;i++) + if(gCarEntities[i]!=gCarEntities[gReplayViewedEntityID]) + { + float posX=(gCarEntities[i]->pos.x-gMapInfo->overviewTopLeft.x)/(gMapInfo->overviewBotRight.x-gMapInfo->overviewTopLeft.x)-0.5; + float posY=(gCarEntities[i]->pos.z-gMapInfo->overviewTopLeft.y)/(gMapInfo->overviewBotRight.y-gMapInfo->overviewTopLeft.y); + glLoadIdentity(); + glTranslatef(0.0f,0.0f,-1.0f); + glTranslatef(x+posX*kOverviewSize,y-posY*kOverviewSize*ratio,0); + glScalef(kDotSize*0.5,kDotSize*0.5,0); + + tCarPhysics *phys=(tCarPhysics*)(gCarEntities[i]->physics); + tCarDefinition *car=&(phys->car); + if(car->numColors) + glColor4f(car->colors[phys->color].x,car->colors[phys->color].y,car->colors[phys->color].z,(1-gConfig->hudTransparency)*opacity); + else + glColor4f(1,1,1,1); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex2f(1,-1); + glTexCoord2d(1,0); glVertex2f(1,1); + glTexCoord2d(0,1); glVertex2f(-1,-1); + glTexCoord2d(0,0); glVertex2f(-1,1); + glEnd(); + } + glColor4f(1,1,1,1); + if(gGameInfo->numLaps==-1) + { + glColor4f(gEnvironment->instrumentColor.x,gEnvironment->instrumentColor.y,gEnvironment->instrumentColor.z,(1-gConfig->hudTransparency)*opacity); + float posX=(gGhostEntity->pos.x-gMapInfo->overviewTopLeft.x)/(gMapInfo->overviewBotRight.x-gMapInfo->overviewTopLeft.x)-0.5; + float posY=(gGhostEntity->pos.z-gMapInfo->overviewTopLeft.y)/(gMapInfo->overviewBotRight.y-gMapInfo->overviewTopLeft.y); + glLoadIdentity(); + glTranslatef(0.0f,0.0f,-1.0f); + glTranslatef(x+posX*kOverviewSize,y-posY*kOverviewSize*ratio,0); + glScalef(kDotSize*0.5,kDotSize*0.5,0); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex2f(1,-1); + glTexCoord2d(1,0); glVertex2f(1,1); + glTexCoord2d(0,1); glVertex2f(-1,-1); + glTexCoord2d(0,0); glVertex2f(-1,1); + glEnd(); + } + + TexturesSelectTex(FileGetReference(kWhiteDiamondFile)); +// TexturesSelectTex(FileGetReference("dot2.pct")); + float posX=(gCarEntities[gReplayViewedEntityID]->pos.x-gMapInfo->overviewTopLeft.x)/(gMapInfo->overviewBotRight.x-gMapInfo->overviewTopLeft.x)-0.5; + float posY=(gCarEntities[gReplayViewedEntityID]->pos.z-gMapInfo->overviewTopLeft.y)/(gMapInfo->overviewBotRight.y-gMapInfo->overviewTopLeft.y); + glLoadIdentity(); + glTranslatef(0.0f,0.0f,-1.0f); + glTranslatef(x+posX*kOverviewSize,y-posY*kOverviewSize*ratio,0); + glScalef(kDotSize*1*0.5,kDotSize*1*0.5,0); + +/* glColor4f(RandomFl(0,1),RandomFl(0,1),RandomFl(0,1),1); + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex2f(1,-1); + glTexCoord2d(1,0); glVertex2f(1,1); + glTexCoord2d(0,1); glVertex2f(-1,-1); + glTexCoord2d(0,0); glVertex2f(-1,1); + glEnd();*/ + + glLoadIdentity(); + glTranslatef(0.0f,0.0f,-1.0f); + glTranslatef(x+posX*kOverviewSize,y-posY*kOverviewSize*ratio,0); + glScalef(kDotSize*2*0.5,kDotSize*2*0.5,0); + + tCarPhysics *phys=(tCarPhysics*)(gCarEntities[gReplayViewedEntityID]->physics); + tCarDefinition *car=&(phys->car); + if(car->numColors) + glColor4f(car->colors[phys->color].x,car->colors[phys->color].y,car->colors[phys->color].z,(1-gConfig->hudTransparency)*opacity); + else + glColor4f(1,1,1,1); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex2f(1,-1); + glTexCoord2d(1,0); glVertex2f(1,1); + glTexCoord2d(0,1); glVertex2f(-1,-1); + glTexCoord2d(0,0); glVertex2f(-1,1); + glEnd(); + + glPopAttrib(); + glPopMatrix(); + gTexturesQualityModifier=0; +} + +#include "error.h" +//display in-game stats +void RaceInfoDisplay(float inOpacity) +{ + if(!gGameInfo->demolition) + { + tCarPhysics *phys=(tCarPhysics*)gViewedEntity->physics; + + if(inOpacity==0) + return; + //if someone has finished the game, print a message on screen + if(gDisqualified) + TextPrintfToBufferFormatedColored(Vector(0,1),0.1,kTextAlignMiddle,1,1,1,inOpacity,"Disqualified!"); + + //if the race has started, print statistics + float opacity=inOpacity*(gFrameCount*kFrameTime-kStartGameDelaySeconds); + if(gGameInfo->maxTime!=0)opacity=inOpacity; + if(opacity>inOpacity)opacity=inOpacity; + if(opacity<0)opacity=0; + + RenderOverview(opacity,0.42,0.3); + RenderCornerSign(); + + if(gMapInfo->loop&&gGameInfo->maxTime==0) + if(gGameInfo->numLaps!=-1) + { + //print last lap time + float lastLapTime=phys->lapCount>1?(phys->lapTimes[phys->lapCount-1]-phys->lapTimes[phys->lapCount-2])*kFrameTime:0; + TextPrintfToBufferFormatedColored(Vector(-1,0.92),0.03,kTextAlignLeft,1,1,1,opacity,"Last Lap: %d:%02d'%02d",((int)lastLapTime)/60,((int)lastLapTime)%60,((int)(lastLapTime*100))%100); + + //print lap counter + if(phys->lapCount<=gGameInfo->numLaps) + TextPrintfToBufferFormatedColored(Vector(1,1),0.05,kTextAlignRight,1,1,1,opacity,"Lap: %d/%d",phys->lapCount,gGameInfo->numLaps); + else + TextPrintfToBufferFormatedColored(Vector(1,1),0.05,kTextAlignRight,1,1,1,opacity,"Lap: %d/%d",gGameInfo->numLaps,gGameInfo->numLaps); + } + else + { + //print last lap time + float lastLapTime=gLastLapTime*kFrameTime; + TextPrintfToBufferFormatedColored(Vector(-1,0.92),0.03,kTextAlignLeft,1,1,1,opacity,"Last Lap: %d:%02d'%02d",((int)lastLapTime)/60,((int)lastLapTime)%60,((int)(lastLapTime*100))%100); + float bestLapTime=gBestLapTime*kFrameTime; + TextPrintfToBufferFormatedColored(Vector(-1,0.84),0.03,kTextAlignLeft,1,1,1,opacity,"Best Lap: %d:%02d'%02d",((int)bestLapTime)/60,((int)bestLapTime)%60,((int)(bestLapTime*100))%100); + float offset=gConfig->registerLapTimes?0.32:0.24; + if(gConfig->registerLapTimes) + if(gWorldRecord!=-1) + { + float worldRecord=gWorldRecord*kFrameTime; + if(worldRecord) + { + TextPrintfToBufferFormatedColored(Vector(-1,0.76),0.03,kTextAlignLeft,1,1,1,opacity,"World Record: %d:%02d'%02d (set by %s\255#n\255)",((int)worldRecord)/60,((int)worldRecord)%60,((int)(worldRecord*100))%100,gRecordName); +// TextPrintfToBufferFormatedColored(Vector(-0.95,0.68),0.03,kTextAlignLeft,1,1,1,opacity,"set by: %s",gRecordName); + } + else if(gWaitingForRecord) + TextPrintfToBufferFormatedColored(Vector(-1,0.76),0.03,kTextAlignLeft,1,1,1,opacity,"World Record: not set"); + else + TextPrintfToBufferFormatedColored(Vector(-1,0.76),0.03,kTextAlignLeft,1,1,1,opacity,"World Record: unavailable"); + } + else + TextPrintfToBufferFormatedColored(Vector(-1,0.76),0.03,kTextAlignLeft,1,1,1,opacity,"World Record: Fetching..."); + + float localRecord=gLocalRecord*kFrameTime; + if(localRecord==0) + TextPrintfToBufferFormatedColored(Vector(-1.0,1.0-offset),0.03,kTextAlignLeft,1,1,1,opacity,"Personal Record: not set"); + else + TextPrintfToBufferFormatedColored(Vector(-1.0,1.0-offset),0.03,kTextAlignLeft,1,1,1,opacity,"Personal Record: %d:%02d'%02d",((int)localRecord)/60,((int)localRecord)%60,((int)(localRecord*100))%100); + + //print lap counter + TextPrintfToBufferFormatedColored(Vector(1,1),0.05,kTextAlignRight,1,1,1,opacity,"Lap: %d",phys->lapCount); + } + + //print current lap time + if(gGameInfo->maxTime==0) + { + float lapTime=phys->lapCount?(gFrameCount-(gGameInfo->numLaps!=-1?(phys->lapTimes[phys->lapCount-1]):gCurrentLapStart))*kFrameTime:0; + TextPrintfToBufferFormatedColored(Vector(-1,1),0.03,kTextAlignLeft,1,1,1,opacity,"This Lap: %d:%02d'%02d",((int)lapTime)/60,((int)lapTime)%60,((int)(lapTime*100))%100); + } + else + { + float timeTaken=gFrameCount*kFrameTime-kStartGameDelaySeconds; + if(timeTaken<0) + timeTaken=0; + TextPrintfToBufferFormatedColored(Vector(-1,1),0.03,kTextAlignLeft,1,1,1,opacity,"Time Taken: %d:%02d'%02d",((int)timeTaken)/60,((int)timeTaken)%60,((int)(timeTaken*100))%100); + float offset=gConfig->registerLapTimes?0.16:0.08; + if(gConfig->registerLapTimes) + if(gWorldRecord!=-1) + { + float worldRecord=gWorldRecord*kFrameTime; + if(worldRecord==0) + TextPrintfToBufferFormatedColored(Vector(-1,0.92),0.03,kTextAlignLeft,1,1,1,opacity,"World Record: not set"); + else if((phys->lapCount<=1?timeTaken:(phys->lapTimes[1]*kFrameTime-kStartGameDelaySeconds))lapCount<=1?timeTaken:(phys->lapTimes[1]*kFrameTime-kStartGameDelaySeconds))lapCount<=1?timeTaken:(phys->lapTimes[1]*kFrameTime-kStartGameDelaySeconds))goldTime) + TextPrintfToBufferFormatedColored(Vector(-1.0,0.92-offset),0.03,kTextAlignLeft,1,1,1,opacity,"\255goldmedal.pct\255: %d:%02d'%02d",((int)gGameInfo->goldTime)/60,((int)gGameInfo->goldTime)%60,((int)(gGameInfo->goldTime*100))%100); + else + TextPrintfToBufferFormatedColored(Vector(-1.0,0.92-offset),0.03,kTextAlignLeft,1,1,1,opacity,"\255goldmedal.pct\255: failed"); + if((phys->lapCount<=1?timeTaken:(phys->lapTimes[1]*kFrameTime-kStartGameDelaySeconds))silverTime) + TextPrintfToBufferFormatedColored(Vector(-1.0,0.84-offset),0.03,kTextAlignLeft,1,1,1,opacity,"\255silvermedal.pct\255: %d:%02d'%02d",((int)gGameInfo->silverTime)/60,((int)gGameInfo->silverTime)%60,((int)(gGameInfo->silverTime*100))%100); + else + TextPrintfToBufferFormatedColored(Vector(-1.0,0.84-offset),0.03,kTextAlignLeft,1,1,1,opacity,"\255silvermedal.pct\255: failed"); + if((phys->lapCount<=1?timeTaken:(phys->lapTimes[1]*kFrameTime-kStartGameDelaySeconds))maxTime) + TextPrintfToBufferFormatedColored(Vector(-1.0,0.76-offset),0.03,kTextAlignLeft,1,1,1,opacity,"\255bronzemedal.pct\255: %d:%02d'%02d",((int)gGameInfo->maxTime)/60,((int)gGameInfo->maxTime)%60,((int)(gGameInfo->maxTime*100))%100); + else + { + TextPrintfToBufferFormatedColored(Vector(-1.0,0.76-offset),0.03,kTextAlignLeft,1,1,1,opacity,"\255bronzemedal.pct\255: failed"); + if(!gDisqualified) + TextPrintfToBufferFormatedColored(Vector(0,1),0.1,kTextAlignMiddle,1,1,1,opacity,"Time Up!"); + } + } + + //print lead/trail + if(gGameInfo->maxTime==0) + { + tVector2 leadPos=Vector(-1,gGameInfo->numLaps!=-1?0.84:(gConfig->registerLapTimes?0.6:0.68)); + if(phys->lead>=0) + TextPrintfToBufferFormatedColored(leadPos,0.03,kTextAlignLeft,1,1,1,opacity,"Lead: %3.1f",phys->lead); + else + TextPrintfToBufferFormatedColored(leadPos,0.03,kTextAlignLeft,1,1,1,opacity,"Trail: %3.1f",-phys->lead); + + if(gGameInfo->numPlayers>1&&gGameInfo->numLaps!=-1) + for(int i=0;inumPlayers;i++) + { + tCarPhysics *phys2=(tCarPhysics*)gCarEntities[i]->physics; + if(phys2->lead>=0&&phys->lead<0) + { + if(gGameInfo->network) + TextPrintfToBufferFormatedColored(leadPos-Vector(0,0.08),0.03,kTextAlignLeft,1,1,1,opacity,"Leader: %s",gGameInfo->playerNames[i]); + else + TextPrintfToBufferFormatedColored(leadPos-Vector(0,0.08),0.03,kTextAlignLeft,1,1,1,opacity,"Leader: %s",phys2->car.carName); + break; + } + else if(phys2->lead==-phys->lead) + { + if(gGameInfo->network) + TextPrintfToBufferFormatedColored(leadPos-Vector(0,0.08),0.03,kTextAlignLeft,1,1,1,opacity,"Follower: %s",gGameInfo->playerNames[i]); + else + TextPrintfToBufferFormatedColored(leadPos-Vector(0,0.08),0.03,kTextAlignLeft,1,1,1,opacity,"Follower: %s",phys2->car.carName); + break; + } + } + + if(gGameInfo->numLaps!=-1) + { + /*if(GetInterfaceKey(kInterfaceKeyEasterEgg)) + { + for(int i=0;inumPlayers;i++) + { + if(gCarEntities[i]->id>=0) + { + tCarPhysics *phys2=(tCarPhysics*)gCarEntities[i]->physics; + PrintConsoleString("%s: lap %d,pos %f, lead %f",FileGetName(gGameInfo->playerCars[i]),phys2->lap,phys2->position,phys2->lead); + } + } + }*/ + //print position + int position=0; + for(int i=0;inumPlayers;i++) + { + if(gCarEntities[i]->id>=0) + { + tCarPhysics *phys2=(tCarPhysics*)gCarEntities[i]->physics; + if(phys2->lap>phys->lap)position++; + else if(gGameInfo->reverse) + {if(phys2->lap==phys->lap&&phys2->position<=phys->position)position++;} + else + {if(phys2->lap==phys->lap&&phys2->position>=phys->position)position++;} + } + } + int numPlayers=0; + for(int i=0;inumPlayers;i++) + if(gCarEntities[i]->id>=0) + numPlayers++; + TextPrintfToBufferFormatedColored(Vector(1,0.9),0.05,kTextAlignRight,1,1,1,opacity,"Position: %d/%d",position,numPlayers); + } + } + +/* if(phys->arcadeDraftBoost>0.2) + { + float a=phys->arcadeDraftBoost; + TextPrintfToBufferFormatedColored(Vector(0,0.7),0.09,kTextAlignMiddle,0,1,0,a,"Drafting"); + }*/ + + if(!gReplay) + //if the race has not yet started print countdown + if(gFrameCount*kFrameTime=kStartGameDelaySeconds) + TextPrintfToBufferFormatedColored(Vector(0,0.15),0.2+0.1*(gFrameCount%(int)kFPS)/kFPS,kTextAlignMiddle,1,1,1,inOpacity*(1-(gFrameCount%(int)kFPS)/kFPS),"GO!"); + else if(gFrameCount*kFrameTime>=kStartGameDelaySeconds-3) + TextPrintfToBufferFormatedColored(Vector(0,0.15),0.05+0.02*(gFrameCount/kFPS)+0.1*(gFrameCount%(int)kFPS)/kFPS,kTextAlignMiddle,1,1,1,inOpacity*(1-(gFrameCount%(int)kFPS)/kFPS),"%d",(int)kStartGameDelaySeconds-gFrameCount/(int)kFPS); + } + else + { + int numDisabled=0; + for(int i=0;inumPlayers;i++) + { + if(gCarEntities[i]->id>=0) + { + tCarPhysics *phys=(tCarPhysics*)gCarEntities[i]->physics; + if(phys->damage>=kFatalDamage) + numDisabled++; + } + } + if(numDisabled>0&&numDisabled>=gGameInfo->numPlayers-1) + { + if(numDisabled==gGameInfo->numPlayers) + TextPrintfToBufferFormatedColored(Vector(0,0.2),0.1,kTextAlignMiddle,1,1,1,inOpacity,"Everyone wrecked!"); + for(int i=0;inumPlayers;i++) + { + if(gCarEntities[i]->id>=0) + { + tCarPhysics *phys=(tCarPhysics*)gCarEntities[i]->physics; + if(phys->damageplayerNames[i]); + } + } + } + else + { + tCarPhysics *phys=(tCarPhysics*)gCarEntities[gGameInfo->playerID]->physics; + if(phys->damage>=kFatalDamage) + TextPrintfToBufferFormatedColored(Vector(0,0.2),0.1,kTextAlignMiddle,1,1,1,inOpacity,"You're wrecked!"); + TextPrintfToBufferFormatedColored(Vector(-1,1),0.03,kTextAlignLeft,1,1,1,inOpacity,"Wrecked: %d/%d",numDisabled,gGameInfo->numPlayers); + } + } +} +/* +void SpeedStatsDisplay(float opacity) +{ + tCarPhysics *phys=(tCarPhysics*)gCarEntities[gGameInfo->playerID]->physics; + + //Print Top Speed and average speed. + if(gConfig->metricUnits){ + TextPrintfToBufferFormatedColored(Vector(0,-0.75),0.03,kTextAlignMiddle,1,1,1,opacity,"Top Speed: %3.0f km/h",phys->maxSpeed*3.6); + TextPrintfToBufferFormatedColored(Vector(0,-0.65),0.03,kTextAlignMiddle,1,1,1,opacity,"Average Speed: %3.0f km/h",phys->averageSpeed*3.6); + } + else{ + TextPrintfToBufferFormatedColored(Vector(0,-0.75),0.03,kTextAlignMiddle,1,1,1,opacity,"Top Speed: %3.0f M.P.H.",phys->maxSpeed*2.2369); + TextPrintfToBufferFormatedColored(Vector(0,-0.65),0.03,kTextAlignMiddle,1,1,1,opacity,"Average Speed: %3.0f M.P.H.",phys->averageSpeed*2.2369); + } +} + +void BestLapsDisplay(float opacity) +{ + if(gMapInfo->loop) + { + tCarPhysics *phys=(tCarPhysics*)gCarEntities[gGameInfo->playerID]->physics; + //print 5 best laps of player + TextPrintfToBufferFormatedColored(Vector(-0.5,0.5),0.03,kTextAlignLeft,1,1,1,opacity,"Best Laps:"); + int bestLaps[5]; + for(int i=0;i<5&&inumLaps;i++) + { + float bestLapTime=INFINITY; + bestLaps[i]=0; + for(int j=0;jnumLaps;j++) + { + float lapTime=(phys->lapTimes[j+1]-phys->lapTimes[j]); + if(lapTime<=0) + lapTime=10000000; + if(lapTimelapTimes[bestLaps[i]+1]-phys->lapTimes[bestLaps[i]]>0) + { + float bestTime=(phys->lapTimes[bestLaps[i]+1]-phys->lapTimes[bestLaps[i]])*kFrameTime; + TextPrintfToBufferFormatedColored(Vector(-0.5,0.4-0.05*i),0.025,kTextAlignLeft,1,1,1,opacity,"%s Lap: %d:%02d'%02d",str,((int)bestTime)/60,((int)bestTime)%60,((int)(bestTime*100))%100); + } + else + TextPrintfToBufferFormatedColored(Vector(-0.5,0.4-0.05*i),0.025,kTextAlignLeft,1,1,1,opacity,"%s Lap: Retired",str); + } + } +} + +void BestDriversDisplay(float opacity) +{ + //print best players + TextPrintfToBufferFormatedColored(Vector(-0.5,-0.1),0.03,kTextAlignLeft,1,1,1,opacity,"Best Drivers:"); + int bestDrivers[kMaxPlayers]; + int numPlayersFinished=0; + for(int i=0;inumPlayers;i++) + { + tCarPhysics *phys=(tCarPhysics*)gCarEntities[i]->physics; + if(phys->lapCount>gGameInfo->numLaps) + { + numPlayersFinished++; + if(!phys->finishTime) + phys->finishTime=phys->lapTimes[gGameInfo->numLaps]; + } + } + if(gReplay) + { + float leaderTime=0; + + float time=0; + for(int i=0;inumLaps;j++) + if(gCheckPoints[i].passTimes[j].time>time) + time=gCheckPoints[i].passTimes[j].time; + float raceLength=kNumCheckPoints*gGameInfo->numLaps+1; + for(int i=0;inumPlayers;i++) + { + tCarPhysics *phys=(tCarPhysics*)gCarEntities[i]->physics; + if(phys->lead>0) + { + float racePosition=phys->checkPoint+(phys->lapCount>1?phys->lapCount-1:0)*kNumCheckPoints+1; + float completeness=racePosition/raceLength; + leaderTime=time*kFPS/completeness; + } + } + + for(int i=0;inumPlayers;i++) + if(gCarEntities[i]->controlType==kControlTypeAIInput) + { + tCarPhysics *phys=(tCarPhysics*)gCarEntities[i]->physics; + if(phys->lapCount<=gGameInfo->numLaps) + { + phys->lapCount=gGameInfo->numLaps+1; + phys->finishTime=leaderTime-phys->lead*kFPS; + numPlayersFinished++; + } + } + } + + for(int i=0;i<(gReplay?gGameInfo->numPlayers:numPlayersFinished);i++) + { + int bestTime=0x7fffffff; + bestDrivers[i]=0; + for(int j=0;jnumPlayers;j++) + { + tCarPhysics *phys=(tCarPhysics*)gCarEntities[j]->physics; + int finishTime=phys->finishTime; + if(finishTime<=0) + finishTime=0x7ffffffe; + if(finishTimephysics; + tCarDefinition *car=&(phys->car); + char *carName=gGameInfo->network?gGameInfo->playerNames[bestDrivers[i]]:car->carName; + if(bestTime!=0x7ffffffe) + { + float t=bestTime*kFrameTime-kStartGameDelaySeconds; + TextPrintfToBufferFormatedColored(Vector(-0.5,-0.2-0.05*i),0.025,kTextAlignLeft,1,1,1,opacity,"%s: %d:%02d'%02d",carName,((int)t)/60,((int)t)%60,((int)(t*100))%100); + } + else + TextPrintfToBufferFormatedColored(Vector(-0.5,-0.2-0.05*i),0.025,kTextAlignLeft,1,1,1,opacity,"%s: Retired",carName); + + //print players place + if(bestDrivers[i]==gGameInfo->playerID) + if(bestTime!=0x7ffffffe) + { + char str[8]; + MakeNumString(i+1,str); + TextPrintfToBufferFormatedColored(Vector(0.0,0.8),0.07,kTextAlignMiddle,1,1,1,opacity,"%s Place!",str); + } + else + TextPrintfToBufferFormatedColored(Vector(0.0,0.8),0.07,kTextAlignMiddle,1,1,1,opacity,"Retired!"); + } +} + +//displays game stats when the player has finished the race +void EndTextDisplay(float opacity) +{ + if(opacity==0) + return; + SpeedStatsDisplay(opacity); + RenderOverview(opacity); + if(gGameInfo->numLaps!=-1) + { + if(gGameInfo->maxTime>0) + { + tCarPhysics *phys=(tCarPhysics*)gViewedEntity->physics; + if(phys->lapCount<=1) + TextPrintfToBufferFormatedColored(Vector(0,1),0.1,kTextAlignMiddle,1,1,1,opacity,"Time Up!"); + else if(phys->lapTimes[1]*kFrameTime-kStartGameDelaySeconds>gGameInfo->maxTime) + TextPrintfToBufferFormatedColored(Vector(0,1),0.1,kTextAlignMiddle,1,1,1,opacity,"Time Up!"); + else + { + if(phys->lapTimes[1]*kFrameTime-kStartGameDelaySeconds<=gGameInfo->goldTime) + TextPrintfToBufferFormatedColored(Vector(0,1),0.1,kTextAlignMiddle,1,1,1,opacity,"\255goldmedal.pct\255Gold Medal!"); + else if(phys->lapTimes[1]*kFrameTime-kStartGameDelaySeconds<=gGameInfo->silverTime) + TextPrintfToBufferFormatedColored(Vector(0,1),0.1,kTextAlignMiddle,1,1,1,opacity,"\255silvermedal.pct\255Silver Medal!"); + else + TextPrintfToBufferFormatedColored(Vector(0,1),0.1,kTextAlignMiddle,1,1,1,opacity,"\255bronzemedal.pct\255Bronze Medal!"); + float t=phys->lapTimes[1]*kFrameTime-kStartGameDelaySeconds; + TextPrintfToBufferFormatedColored(Vector(0.0,0.6),0.035,kTextAlignMiddle,1,1,1,opacity,"Time taken: %d:%02d'%02d",((int)t)/60,((int)t)%60,((int)(t*100))%100); + } + } + else + { + BestLapsDisplay(opacity); + BestDriversDisplay(opacity); + } + if(gGameInfo->network&&gGameInfo->playerID!=0) + TextPrintfToBufferFormatedColored(Vector(0,-0.86),0.05,kTextAlignMiddle,1,1,1,opacity,"Hit esc to leave game."); + else + TextPrintfToBufferFormatedColored(Vector(0,-0.86),0.05,kTextAlignMiddle,1,1,1,opacity,"Hit esc to exit."); + } +} +*/ + + +void SpeedStatsDisplay(float opacity) +{ + tCarPhysics *phys=(tCarPhysics*)gCarEntities[gGameInfo->playerID]->physics; + + //Print Top Speed and average speed. + if(gConfig->metricUnits){ + TextPrintfToBufferFormatedColored(Vector(-0.9,-0.5),0.03,kTextAlignLeft,1,1,1,opacity,"Top Speed: %3.0f km/h",phys->maxSpeed*3.6); + TextPrintfToBufferFormatedColored(Vector(-0.9,-0.6),0.03,kTextAlignLeft,1,1,1,opacity,"Average Speed: %3.0f km/h",phys->averageSpeed*3.6); + } + else{ + TextPrintfToBufferFormatedColored(Vector(-0.9,-0.5),0.03,kTextAlignLeft,1,1,1,opacity,"Top Speed: %3.0f M.P.H.",phys->maxSpeed*2.2369); + TextPrintfToBufferFormatedColored(Vector(-0.9,-0.6),0.03,kTextAlignLeft,1,1,1,opacity,"Average Speed: %3.0f M.P.H.",phys->averageSpeed*2.2369); + } +} + +void BestLapsDisplay(float opacity) +{ + if(gMapInfo->loop) + { + tCarPhysics *phys=(tCarPhysics*)gCarEntities[gGameInfo->playerID]->physics; + //print 5 best laps of player + TextPrintfToBufferFormatedColored(Vector(0.9,0.45),0.03,kTextAlignRight,1,1,1,opacity,"Best Laps:"); + int bestLaps[5]; + for(int i=0;i<5&&inumLaps;i++) + { + float bestLapTime=INFINITY; + bestLaps[i]=0; + for(int j=0;jnumLaps;j++) + { + float lapTime=(phys->lapTimes[j+1]-phys->lapTimes[j]); + if(lapTime<=0) + lapTime=10000000; + if(lapTimelapTimes[bestLaps[i]+1]-phys->lapTimes[bestLaps[i]]>0) + { + float bestTime=(phys->lapTimes[bestLaps[i]+1]-phys->lapTimes[bestLaps[i]])*kFrameTime; + TextPrintfToBufferFormatedColored(Vector(0.9,0.35-0.05*i),0.025,kTextAlignRight,1,1,1,opacity,"%s Lap: %d:%02d'%02d",str,((int)bestTime)/60,((int)bestTime)%60,((int)(bestTime*100))%100); + } + else + TextPrintfToBufferFormatedColored(Vector(0.9,0.35-0.05*i),0.025,kTextAlignRight,1,1,1,opacity,"%s Lap: Retired",str); + } + } +} + +void BestDriversDisplay(float opacity) +{ + //print best players + TextPrintfToBufferFormatedColored(Vector(0.9,-0.05),0.03,kTextAlignRight,1,1,1,opacity,"Best Drivers:"); + int bestDrivers[kMaxPlayers]; + int numPlayersFinished=0; + for(int i=0;inumPlayers;i++) + { + tCarPhysics *phys=(tCarPhysics*)gCarEntities[i]->physics; + if(phys->lapCount>gGameInfo->numLaps) + numPlayersFinished++; + } + if(gReplay) + { + float leaderTime=0; + + float time=0; + for(int i=0;inumLaps;j++) + if(gCheckPoints[i].passTimes[j].time>time) + time=gCheckPoints[i].passTimes[j].time; + float raceLength=kNumCheckPoints*gGameInfo->numLaps+1; + for(int i=0;inumPlayers;i++) + { + tCarPhysics *phys=(tCarPhysics*)gCarEntities[i]->physics; + if(phys->lead>0) + { + float racePosition=phys->checkPoint+(phys->lapCount>1?phys->lapCount-1:0)*kNumCheckPoints+1; + float completeness=racePosition/raceLength; + leaderTime=time*kFPS/completeness; + } + } + + /* for(int i=0;inumPlayers;i++) + if(gCarEntities[i]->controlType==kControlTypeAIInput) + { + tCarPhysics *phys=(tCarPhysics*)gCarEntities[i]->physics; + if(phys->lapCount<=gGameInfo->numLaps) + { + phys->lapCount=gGameInfo->numLaps+1; + phys->finishTime=leaderTime-phys->lead*kFPS; + numPlayersFinished++; + } + }*/ + } + + for(int i=0;i<(gReplay?gGameInfo->numPlayers:numPlayersFinished);i++) + { + int bestTime=0x7fffffff; + bestDrivers[i]=0; + for(int j=0;jnumPlayers;j++) + { + tCarPhysics *phys=(tCarPhysics*)gCarEntities[j]->physics; + int finishTime=phys->finishTime; + if(finishTime<=0) + finishTime=0x7ffffffe; + if(finishTimephysics; + tCarDefinition *car=&(phys->car); + char *carName=gGameInfo->network?gGameInfo->playerNames[bestDrivers[i]]:car->carName; + if(bestTime!=0x7ffffffe) + { + float t=bestTime*kFrameTime-kStartGameDelaySeconds; + if(bestDrivers[i]==gGameInfo->playerID) + TextPrintfToBufferFormatedColored(Vector(0.9,-0.15-0.05*i),0.025,kTextAlignRight,0,1,0,opacity,"%s: %d:%02d'%02d",carName,((int)t)/60,((int)t)%60,((int)(t*100))%100); + else + TextPrintfToBufferFormatedColored(Vector(0.9,-0.15-0.05*i),0.025,kTextAlignRight,1,1,1,opacity,"%s: \255#w\255%d:%02d'%02d",carName,((int)t)/60,((int)t)%60,((int)(t*100))%100); + } + else + if(bestDrivers[i]==gGameInfo->playerID) + TextPrintfToBufferFormatedColored(Vector(0.9,-0.15-0.05*i),0.025,kTextAlignRight,0,1,0,opacity,"%s: Retired",carName); + else + TextPrintfToBufferFormatedColored(Vector(0.9,-0.15-0.05*i),0.025,kTextAlignRight,1,1,1,opacity,"%s: \255#w\255Retired",carName); + + //print players place + if(bestDrivers[i]==gGameInfo->playerID) + if(bestTime!=0x7ffffffe) + { + char str[8]; + MakeNumString(i+1,str); + TextPrintfToBufferFormatedColored(Vector(-0.9,0.95),0.08,kTextAlignLeft,1,1,1,opacity,"%s Place!",str); + } + else + TextPrintfToBufferFormatedColored(Vector(-0.9,0.95),0.08,kTextAlignLeft,1,1,1,opacity,"Retired!"); + } +} + +//displays game stats when the player has finished the race +void EndTextDisplay(float opacity) +{ + if(opacity==0) + return; + if(!gReplay) + SpeedStatsDisplay(opacity); + RenderOverview(opacity,0.42,0.36); + if(gGameInfo->numLaps!=-1) + { + if(gGameInfo->maxTime>0) + { + tCarPhysics *phys=(tCarPhysics*)gViewedEntity->physics; + float t=phys->lapTimes[1]*kFrameTime-kStartGameDelaySeconds; + if(gDisqualified) + TextPrintfToBufferFormatedColored(Vector(0,1),0.1,kTextAlignMiddle,1,1,1,opacity,"Disqualified!"); + else if(phys->lapCount<=1) + TextPrintfToBufferFormatedColored(Vector(0,1),0.1,kTextAlignMiddle,1,1,1,opacity,"Time Up!"); + else + { + if(t<=gGameInfo->goldTime) + TextPrintfToBufferFormatedColored(Vector(0,1),0.1,kTextAlignMiddle,1,1,1,opacity,"\255goldmedal.pct\255 Gold Medal!"); + else if(t<=gGameInfo->silverTime) + TextPrintfToBufferFormatedColored(Vector(0,1),0.1,kTextAlignMiddle,1,1,1,opacity,"\255silvermedal.pct\255 Silver Medal!"); + else if(t<=gGameInfo->maxTime) + TextPrintfToBufferFormatedColored(Vector(0,1),0.1,kTextAlignMiddle,1,1,1,opacity,"\255bronzemedal.pct\255 Bronze Medal!"); + else + TextPrintfToBufferFormatedColored(Vector(0,1),0.1,kTextAlignMiddle,1,1,1,opacity,"Time Up!"); + } + if(phys->lapCount>1) + { + TextPrintfToBufferFormatedColored(Vector(0.0,0.6),0.035,kTextAlignMiddle,1,1,1,opacity,"Time taken: %d:%02d'%02d",((int)t)/60,((int)t)%60,((int)(t*100))%100); + if(t>gGameInfo->maxTime&&!gDisqualified) + { + t=t-gGameInfo->maxTime; + TextPrintfToBufferFormatedColored(Vector(0.0,0.52),0.035,kTextAlignMiddle,1,1,1,opacity,"Improve by: %d:%02d'%02d",((int)t)/60,((int)t)%60,((int)(t*100))%100); + } + } + } + else + { + BestLapsDisplay(opacity); + BestDriversDisplay(opacity); + } + if(gGameInfo->network&&gGameInfo->playerID!=0) + TextPrintfToBufferFormatedColored(Vector(-0.9,-0.77),0.029,kTextAlignLeft,1,1,1,opacity,"Hit esc to leave game."); + else + TextPrintfToBufferFormatedColored(Vector(-0.9,-0.77),0.029,kTextAlignLeft,1,1,1,opacity,"Hit esc to exit."); + if(gGameInfo->network) + TextPrintfToBufferFormatedColored(Vector(0.9,-0.77),0.029,kTextAlignRight,1,1,1,opacity,"Watching %s",gGameInfo->playerNames[gReplayViewedEntityID]); + else + { + tCarDefinition *car=(tCarDefinition*)FileGetParsedDataPtr(gGameInfo->playerCars[gReplayViewedEntityID],kParserTypeCarDesc,sizeof(tCarDefinition)); + TextPrintfToBufferFormatedColored(Vector(0.9,-0.77),0.029,kTextAlignRight,1,1,1,opacity,"Watching %s",car->carName); + } + } +} + +void DimScreen(float opacity) +{ + if(opacity==0) + return; + glPushAttrib(GL_DEPTH_BUFFER_BIT+GL_LIGHTING_BIT+GL_COLOR_BUFFER_BIT+GL_TEXTURE_BIT+GL_CURRENT_BIT); + + glColor4f(0,0,0,0.3*opacity*(1-gConfig->hudTransparency)); + + glLoadIdentity(); + + glDisable(GL_LIGHTING); + glDisable(GL_TEXTURE_2D); + glDisable(GL_DEPTH_TEST); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glBegin(GL_TRIANGLE_STRIP); + glVertex3f(-1.0f, 1.0f,-1.00f); + glVertex3f(-1.0f,-1.0f,-1.00f); + glVertex3f( 1.0f, 1.0f,-1.00f); + glVertex3f( 1.0f,-1.0f,-1.00f); + glEnd(); + + glPopAttrib(); +} + +#define kChatScreenTime 9.0 +#define kChatFadeTime 6.0 + +void ChatDisplay() +{ + if(gInputChatMode) + { + int offset=0; + while(TextWidth(gGameChatMessage+offset,0.04)>1.5*kTextXPos) + offset++; + TextPrintfToBufferFormated(Vector(-0.75,0.5),0.04,kTextAlignLeft,"chat: %s%s",gGameChatMessage+offset,(int)(gFrameCount*kFrameTime*4)%2?"_":""); + } + float t=TimeGetSeconds(); + + while((t-gGameChatMessagesRecv[0].timestamp)>kChatScreenTime&&gNumGameChatMessages) + GameChatMessagesScroll(); + + int pos=0; + for(int i=0;i1)a=1; + if(a<0)a=0; + if((t-gGameChatMessagesRecv[i].timestamp)<0.2) + a=(t-gGameChatMessagesRecv[i].timestamp)/0.2; + + char str[256]; + int offset=0; + do{ + strcpy(str,gGameChatMessagesRecv[i].message+offset); + int word=true; + while(TextWidth(str,0.03)>0.9*kTextXPos) + { + word=str[strlen(str)-1]==' '; + str[strlen(str)-1]='\0'; + } + if(!word) + { + int i=strlen(str)-1; + while(str[i]!=' '&&i>0) + i--; + if(i>0) + str[i]='\0'; + } + offset+=strlen(str); + if(offset) + TextPrintfToBufferFormatedColored(Vector(-0.4,1.0-pos*0.07),0.03,kTextAlignLeft,1,1,1,a,"\255#a\255%s",str); + else + TextPrintfToBufferFormatedColored(Vector(-0.4,1.0-pos*0.07),0.03,kTextAlignLeft,1,1,1,a,str); + pos++; + }while(offsetnetwork&&gGameInfo->playerID!=0) + TextPrintfToBufferFormatedColored(Vector(-0.8,0.7),0.06,kTextAlignLeft,1,1,1,opacity,"Are you sure you want to leave the game?"); + else + TextPrintfToBufferFormatedColored(Vector(-0.8,0.7),0.06,kTextAlignLeft,1,1,1,opacity,"Are you sure you want to end the game?"); + TextPrintfToBufferFormatedColored(Vector(-0.6,0.35),0.05,kTextAlignLeft,1,1,1,opacity,"Cancel"); + if(gInputEscMode==kInputEscModeQuit) + TextPrintfToBufferFormatedColored(Vector(-0.6,0.5),0.05,kTextAlignLeft,1,1,1,opacity,"Quit"); + else + TextPrintfToBufferFormatedColored(Vector(-0.6,0.5),0.05,kTextAlignLeft,1,1,1,opacity,"Exit"); + if(!gGameInfo->network) + TextPrintfToBufferFormatedColored(Vector(-0.6,0.2),0.05,kTextAlignLeft,1,1,1,opacity,"Retry"); + + + glLoadIdentity(); + glTranslatef(0.0f,0.0f,-1.0f); + + glPushAttrib(GL_DEPTH_BUFFER_BIT+GL_LIGHTING_BIT+GL_COLOR_BUFFER_BIT+GL_CURRENT_BIT); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + + TexturesSelectTex(FileGetReference(kInterfaceMenuPointerTexture)); + + glColor4f(1,1,1,opacity); + glBegin(GL_TRIANGLE_STRIP); + float y=((gInputEscSelection?0.5:0.35)-0.05)*kScreenYPos; + if(!gGameInfo->network&&gInputEscSelection==2) + y=(0.2-0.05)*kScreenYPos; + glTexCoord2d(1,1); glVertex2f((-0.6-0.05)*kScreenXPos+kInterfaceMenuPointerSize,y-kInterfaceMenuPointerSize); + glTexCoord2d(1,0); glVertex2f((-0.6-0.05)*kScreenXPos+kInterfaceMenuPointerSize,y+kInterfaceMenuPointerSize); + glTexCoord2d(0,1); glVertex2f((-0.6-0.05)*kScreenXPos-kInterfaceMenuPointerSize,y-kInterfaceMenuPointerSize); + glTexCoord2d(0,0); glVertex2f((-0.6-0.05)*kScreenXPos-kInterfaceMenuPointerSize,y+kInterfaceMenuPointerSize); + glEnd(); + + glPopAttrib(); + +} + +void ReplayDisplay(float opacity) +{ + TextPrintfToBufferFormatedColored(Vector(-0.9,0.95),0.08,kTextAlignLeft,1,1,1,opacity*(sin(TimeGetSeconds()*3)*0.5+0.5),"Replay"); + if(gReplayAutoCam) + TextPrintfToBufferFormatedColored(Vector(-0.9,0.77),0.029,kTextAlignLeft,1,1,1,opacity,"camera automatic"); + { + tCarDefinition *car=(tCarDefinition*)FileGetParsedDataPtr(gGameInfo->playerCars[gReplayViewedEntityID],kParserTypeCarDesc,sizeof(tCarDefinition)); + tCarPhysics *phys=(tCarPhysics*)gCarEntities[gReplayViewedEntityID]->physics; + if(phys->plateName) + TextPrintfToBufferFormatedColored(Vector(0.9,-0.77),0.029,kTextAlignRight,1,1,1,opacity,"Watching %s",phys->plateName); + else + TextPrintfToBufferFormatedColored(Vector(0.9,-0.77),0.029,kTextAlignRight,1,1,1,opacity,"Watching %s",car->carName); + } + if(gGameInfo->playerID==0) + TextPrintfToBufferFormatedColored(Vector(-0.9,-0.77),0.029,kTextAlignLeft,1,1,1,opacity,"Hit esc to exit."); + if(gGameInfo->maxTime) + { + float timeTaken=gFrameCount*kFrameTime-kStartGameDelaySeconds; + tCarPhysics *phys=(tCarPhysics*)gViewedEntity->physics; + if(phys->lapCount>1) + timeTaken=phys->lapTimes[1]*kFrameTime-kStartGameDelaySeconds; + if(timeTaken<0) + timeTaken=0; + TextPrintfToBufferFormatedColored(Vector(0.5,0.9),0.03,kTextAlignLeft,1,1,1,opacity,"Time Taken: %d:%02d'%02d",((int)timeTaken)/60,((int)timeTaken)%60,((int)(timeTaken*100))%100); + } + + if(gGameInfo->numPlayers>1) + { + TextPrintfToBufferFormatedColored(Vector(0.73,0.4),0.025,kTextAlignRight,1,1,1,opacity,"Player"); + TextPrintfToBufferFormatedColored(Vector(0.8,0.4),0.025,kTextAlignRight,1,1,1,opacity,"Lap"); + TextPrintfToBufferFormatedColored(Vector(0.9,0.4),0.025,kTextAlignRight,1,1,1,opacity,"Trail"); + + int playerRanks[kMaxPlayers]; + for(int i=0;inumPlayers;i++) + playerRanks[i]=i; + for(int i=0;inumPlayers-1;i++) + for(int j=0;jnumPlayers-1-i;j++) + { + tCarPhysics *phys2=(tCarPhysics*)gCarEntities[playerRanks[j]]->physics; + tCarPhysics *phys1=(tCarPhysics*)gCarEntities[playerRanks[j+1]]->physics; + + if(phys1->finishTime>0&&phys2->finishTime<=0) + { + int tmp=playerRanks[j]; + playerRanks[j]=playerRanks[j+1]; + playerRanks[j+1]=tmp; + } + else if(phys2->finishTime>0&&phys1->finishTime<=0) + ; + else if(phys2->finishTime>0&&phys1->finishTime>0) + { + if(phys2->finishTime>phys1->finishTime) + { + int tmp=playerRanks[j]; + playerRanks[j]=playerRanks[j+1]; + playerRanks[j+1]=tmp; + } + } + else if((phys1->lap>phys2->lap)||(gGameInfo->reverse&&(phys2->lap==phys1->lap&&phys1->positionposition))||(!gGameInfo->reverse&&(phys2->lap==phys1->lap&&phys1->position>phys2->position))) + { + int tmp=playerRanks[j]; + playerRanks[j]=playerRanks[j+1]; + playerRanks[j+1]=tmp; + } + } + + + for(int i=0;inumPlayers;i++) + { + tCarPhysics *phys=(tCarPhysics*)gCarEntities[playerRanks[i]]->physics; + tCarDefinition *car=(tCarDefinition*)FileGetParsedDataPtr(gGameInfo->playerCars[playerRanks[i]],kParserTypeCarDesc,sizeof(tCarDefinition)); + char name[512]; + if(playerRanks[i]==gReplayViewedEntityID) + sprintf(name,"*%s",phys->plateName?phys->plateName:car->carName); + else + sprintf(name,phys->plateName?phys->plateName:car->carName); + + if(playerRanks[i]==gGameInfo->playerID) + { + TextPrintfToBufferFormatedColored(Vector(0.73,0.33-0.05*i),0.025,kTextAlignRight,0,1,0,opacity,name); + if(phys->lapCount<=gGameInfo->numLaps) + TextPrintfToBufferFormatedColored(Vector(0.8,0.33-0.05*i),0.025,kTextAlignRight,0,1,0,opacity,"%d",phys->lapCount); + else + TextPrintfToBufferFormatedColored(Vector(0.8,0.33-0.05*i),0.025,kTextAlignRight,0,1,0,opacity,"F"); + if(phys->lead<0) + TextPrintfToBufferFormatedColored(Vector(0.9,0.33-0.05*i),0.025,kTextAlignRight,0,1,0,opacity,"%03.1f",-phys->lead); + else + TextPrintfToBufferFormatedColored(Vector(0.9,0.33-0.05*i),0.025,kTextAlignRight,0,1,0,opacity,"%03.1f",0); + } + else + { + TextPrintfToBufferFormatedColored(Vector(0.73,0.33-0.05*i),0.025,kTextAlignRight,1,1,1,opacity,name); + if(phys->lapCount<=gGameInfo->numLaps) + TextPrintfToBufferFormatedColored(Vector(0.8,0.33-0.05*i),0.025,kTextAlignRight,1,1,1,opacity,"\255#a\255%d",phys->lapCount); + else + TextPrintfToBufferFormatedColored(Vector(0.8,0.33-0.05*i),0.025,kTextAlignRight,1,1,1,opacity,"\255#a\255F"); + if(phys->lead<0) + TextPrintfToBufferFormatedColored(Vector(0.9,0.33-0.05*i),0.025,kTextAlignRight,1,1,1,opacity,"\255#a\255%03.1f",-phys->lead); + else + TextPrintfToBufferFormatedColored(Vector(0.9,0.33-0.05*i),0.025,kTextAlignRight,1,1,1,opacity,"\255#a\255%03.1f",0); + } + } + } + RenderOverview(opacity,0.42,0.36); +} + +float gEscDisplay=0; +float gPauseDisplay=0; +float gEndDisplay=0; +float gInterfaceFadeDisplay=1; +float gLastDisplayTime=0; +float gReplayDisplay=0; + +#define kFadeSpeed 1.5 + +void CalcFades() +{ + float dt=TimeGetSeconds()-gLastDisplayTime; + gLastDisplayTime+=dt; + if(dt<0)dt=0; + + if(gInputEscMode) + gEscDisplay+=dt*kFadeSpeed; + else + gEscDisplay-=dt*kFadeSpeed; + if(gEscDisplay>1)gEscDisplay=1; + else if(gEscDisplay<0)gEscDisplay=0; + + tCarPhysics *phys=(tCarPhysics*)gViewedEntity->physics; + if(phys->lapCount>gGameInfo->numLaps&&gGameInfo->numLaps!=-1&&!gReplay) + gEndDisplay+=dt*kFadeSpeed; + else + gEndDisplay-=dt*kFadeSpeed; + if(gEndDisplay>1)gEndDisplay=1; + else if(gEndDisplay<0)gEndDisplay=0; + + if(gPaused&&!gInputEscMode) + gPauseDisplay+=dt*kFadeSpeed; + else + gPauseDisplay-=dt*kFadeSpeed; + if(gPauseDisplay>1)gPauseDisplay=1; + else if(gPauseDisplay<0)gPauseDisplay=0; + + if(dt<1) + gInterfaceFadeDisplay-=dt*kFadeSpeed; + if(gInterfaceFadeDisplay<0)gInterfaceFadeDisplay=0; + + if(gReplay) + gReplayDisplay+=dt*kFadeSpeed; + else + gReplayDisplay-=dt*kFadeSpeed; + if(gReplayDisplay>1)gReplayDisplay=1; + else if(gReplayDisplay<0)gReplayDisplay=0; + +} + + +void InitInfoDisplay() +{ + gEscDisplay=0; + gPauseDisplay=0; + gEndDisplay=0; + gInterfaceFadeDisplay=1; + gReplayDisplay=0; + gLastDisplayTime=0; +} + +//Show game stats +void GameShowInfo() +{ + CalcFades(); + + //if enabled, print frame rate statistics on screen + if(gConfig->performanceStats) + { + TextPrintfToBufferFormated(Vector(-0.35,-0.95),0.025,kTextAlignLeft,"FPS :%5.1f",gFPS); + if(gGameInfo->network) + { + char netStr[7]; + NetworkGetStatusString(netStr); + TextPrintfToBufferFormated(Vector(0.15,-0.95),0.025,kTextAlignLeft,netStr); + float rcv,snd; + NetworkGetBandwidth(&rcv,&snd); + TextPrintfToBufferFormated(Vector(-0.1,-0.95),0.025,kTextAlignLeft,"R%3.1f/S%3.1f",rcv,snd); + } + #ifdef __POLYCOUNT + TextPrintfToBufferFormated(Vector(-0.1,-0.95),0.025,kTextAlignLeft,"TPS :%7.0f",gPolyCount*gFPS); + TextPrintfToBufferFormated(Vector(0.15,-0.95),0.025,kTextAlignLeft,"Polys:%d",gPolyCount); + #endif + } + + + //are we still playing, or have we finished the race? + + ChatDisplay(); + EndTextDisplay(gEndDisplay); + + CarRenderPanels(gViewedEntity,1-gPauseDisplay-gEscDisplay-gEndDisplay-gReplayDisplay); + RaceInfoDisplay(1-gPauseDisplay-gEscDisplay-gEndDisplay-gReplayDisplay); + + TextPrintfToBufferFormatedColored(Vector(0,0.7),0.1,kTextAlignMiddle,1,1,1,gPauseDisplay,"Game Paused"); + TextPrintfToBufferFormatedColored(Vector(0,-0.3),0.05,kTextAlignMiddle,1,1,1,gPauseDisplay,"Press \255#r\255%s\255#n\255 to Continue.",gConfig->keys[kInputPause].identifier); + + ReplayDisplay(gReplayDisplay); + + float dim=gEscDisplay+gPauseDisplay+gEndDisplay; + if(dim>1)dim=1; + DimScreen(dim); + EscDisplay(gEscDisplay); + + float a=2-(TimeGetSeconds()-giTunesLastStatusUpdate)*0.4; + if(a>0) + { + TextPrintfToBufferFormatedColored(Vector(0,-0.5),0.25,kTextAlignMiddle,1,1,1,a,"\255stereo.pct\255"); + if(giTunesPlaying==kITunesPlaying) + { + TextPrintfToBufferFormatedColored(Vector(0,-0.65),0.04,kTextAlignMiddle,1,1,1,a,giTunesArtist); + TextPrintfToBufferFormatedColored(Vector(0,-0.75),0.04,kTextAlignMiddle,1,1,1,a,giTunesSong); + } + else if(giTunesPlaying==kITunesPaused) + TextPrintfToBufferFormatedColored(Vector(0,-0.65),0.04,kTextAlignMiddle,1,1,1,a,"Paused"); + else + TextPrintfToBufferFormatedColored(Vector(0,-0.65),0.04,kTextAlignMiddle,1,1,1,a,"Stopped"); + } + + TextPrintBuffer(1-gConfig->hudTransparency,true); + TextClearBuffer(); + + InterfaceDrawBackgroundFade(gInterfaceFadeDisplay,true); +} diff --git a/source/initexit.cpp b/source/initexit.cpp new file mode 100755 index 0000000..3ccbe42 --- /dev/null +++ b/source/initexit.cpp @@ -0,0 +1,157 @@ +//initexit.cpp +//Initialize everything for the game to run and dispose everything +//after the game has terminated + +#include +#include "fpu_exc.h" +#include "screen.h" +#include "fileio.h" +#include "textures.h" +#include "models.h" +#include "text.h" +#include "gamesound.h" +#include "random.h" +#include "controls.h" +#include "network.h" +#include "config.h" +#include "environment.h" +#include "interface.h" +#include "writeout.h" +#include "music.h" +#include "interfaceutil.h" +#include "gametime.h" +#include "error.h" +#include "gamesystem.h" +#include "initexit.h" +#include "tracker.h" +#include "transparency.h" +#include "log.h" + +#include "stdtypes.h" +#include "reg_tool_3.h" +#include "rt3_redline.h" +#include "ASWRegistrationCarbonAPI.h" +#include "file_tool.h" +#include "localtracker.h" + +int IsInFront(); + +void ShowRegistrationScreen() +{ + HandleError(RT3_Open(true,VERSION_3_CODES,RT3_PRODUCT_NAME,"Redline")); + Bool8 launched; + UInt64 code=RT3_GetLicenseCode(); +/* int test=false; + qRT3_LicenseIsSameCode(code,RT3_PIRATED_CODE_01,test); + printf("license test: %d\n",test);*/ + + int valid=true; + int invalid=false; + if(!invalid)qRT3_LicenseIsSameCode(code,RT3_PIRATED_CODE_01,invalid); + if(!invalid)qRT3_LicenseIsSameCode(code,RT3_PIRATED_CODE_02,invalid); + if(!invalid)qRT3_LicenseIsSameCode(code,RT3_PIRATED_FAKE_03,invalid); + if(!invalid)qRT3_LicenseIsSameCode(code,RT3_PIRATED_FAKE_04,invalid); + if(!invalid)qRT3_LicenseIsSameCode(code,RT3_PIRATED_FAKE_05,invalid); + if(!invalid)qRT3_LicenseIsSameCode(code,RT3_PIRATED_FAKE_06,invalid); + if(!invalid)qRT3_LicenseIsSameCode(code,RT3_PIRATED_FAKE_07,invalid); + if(!invalid)qRT3_LicenseIsSameCode(code,RT3_PIRATED_FAKE_08,invalid); + if(!invalid)qRT3_LicenseIsSameCode(code,RT3_PIRATED_FAKE_09,invalid); + if(!invalid)qRT3_LicenseIsSameCode(code,RT3_PIRATED_FAKE_10,invalid); + if(!invalid)qRT3_LicenseIsSameCode(code,RT3_PIRATED_FAKE_11,invalid); + if(!invalid)qRT3_LicenseIsSameCode(code,RT3_PIRATED_FAKE_12,invalid); + if(!invalid)qRT3_LicenseIsSameCode(code,RT3_PIRATED_FAKE_13,invalid); + if(!invalid)qRT3_LicenseIsSameCode(code,RT3_PIRATED_FAKE_14,invalid); + if(!invalid)qRT3_LicenseIsSameCode(code,RT3_PIRATED_FAKE_15,invalid); + if(!invalid)qRT3_LicenseIsSameCode(code,RT3_PIRATED_FAKE_16,invalid); + if(!invalid)qRT3_LicenseIsSameCode(code,RT3_PIRATED_FAKE_17,invalid); + if(!invalid)qRT3_LicenseIsSameCode(code,RT3_PIRATED_FAKE_18,invalid); + if(!invalid)qRT3_LicenseIsSameCode(code,RT3_PIRATED_FAKE_19,invalid); + if(!invalid)qRT3_LicenseIsSameCode(code,RT3_PIRATED_FAKE_20,invalid); + valid=!invalid; + if(!valid) + FT_FileDelete(kPathRefPreferences, "Redline License"); + if(!RT3_IsRegistered()) + { + ASWReg_Initialization(); + ASWReg_ShowDialog(true); + } +/* if(RT3_DisplayNotice(!valid,&launched)==nsvErr) + { + short hit; + AlertStdAlertParamRec alertParam={ + false,false,nil, + "\pExit", + nil, + nil, + kAlertStdAlertOKButton, + 0, + kWindowDefaultPosition}; + StandardAlert(kAlertStopAlert, + "\pCan't find Register Redline app.", + "\pMake sure the Register Redline application is in the same folder as Redline, or reinstall Redline.", + &alertParam, + &hit); + ExitToShell(); + } + if(launched) + ExitToShell(); + if(!IsInFront()) + { + gSystemSuspended=true; + } */ +} + +void CarSelectionDrawCar(tFileRef carRef,float time,int addOns,int color); + +void Init() +{ + SystemInit(); + //EnableFPUExceptions(); + ShowRegistrationScreen(); + FileInitIO(); + + ConfigInit(); + InitTransparency(); + LogInit(); + NetworkInit(); + TrackerStartVersionCheck(); + ScreenInit(); + InitLocalTracker(); + + InterfaceFadeInImageFromBlack(0.291,0.57,FileGetReference("ambrosia.pct"),0.6); + MusicInit(); + float startTime=TimeGetSeconds(); + ControlInit(); + SoundInit(); + LoadAllSounds(); + RandomInit(); + + + TextLoadFont(FileGetReference("test.font")); + LoadEnvironment(FileGetReference("showroom.senv")); + while(!(TimeGetSeconds()>startTime+1)); + InterfaceFadeInImageFromImage(0.291,0.57,FileGetReference("ambrosia.pct"),1,1,FileGetReference("logo.pct"),1.0); +// TexturesSelectTex(FileGetReference("fence1.tif")); +// while(!(TimeGetSeconds()>startTime+3)); + InterfaceInit(); + if(gFileTampered) + { + InterfaceDisplayMessage(-1,"Data files corrupted.","Please re-install Redline."); + Exit(); + } + FlushKeys(); +} + +void Exit() +{ + SoundSilence(); + MusicStopSong(); + ControlExit(); + NetworkStopAdvertising(); + NetworkExit(); + WriteOutFile(FileGetReference(kConfigFileName),gConfig,kParserTypeConfigDesc); + ScreenExit(); + RT3_Close(); + SystemExit(); + ExitToShell(); +} \ No newline at end of file diff --git a/source/initexit.h b/source/initexit.h new file mode 100755 index 0000000..aaefc81 --- /dev/null +++ b/source/initexit.h @@ -0,0 +1,13 @@ +#ifndef __INITEXIT +#define __INITEXIT + +//http://developer.apple.com/technotes/tn/tn1132.html + +#define kVersion 0x01058005 +#define kMinNetworkCompatibleVersion 0x01038000 +#define kVersionString "1.0.5" + +void Init(); +void Exit(); + +#endif \ No newline at end of file diff --git a/source/interface.cpp b/source/interface.cpp new file mode 100644 index 0000000..059a181 --- /dev/null +++ b/source/interface.cpp @@ -0,0 +1,555 @@ +//interface.cpp +//the game's main menu screen + +#include +#include "gametime.h" +#include +#include "fileio.h" +#include "textures.h" +#include "controls.h" +#include "screen.h" +#include "gameinitexit.h" +#include "gameframe.h" +#include "initexit.h" +#include "text.h" +#include "config.h" +#include "interface.h" +#include "carselection.h" +#include "mapselection.h" +#include "random.h" +#include "environment.h" +#include "writeout.h" +#include "challenges.h" +#include "interfaceutil.h" +#include "renderframe.h" +#include "log.h" +#include "gamesystem.h" +#include "gamesound.h" +#include "interfacemultiplayer.h" +#include "tracker.h" + +#include "stdtypes.h" +#include "reg_tool_3.h" +#include "rt3_redline.h" + +tGameInfo gInterfaceGInfo; + +//initializes the user interface +void InterfaceInit() +{ + glClear(GL_COLOR_BUFFER_BIT); + glLoadIdentity(); + glTranslatef(0.0f,0.0f,-1.0f); +} + +void StartBackgroundReplay() +{ + gMapInfo=(tMapInfo*)FileGetParsedDataPtr(gInterfaceGInfo.map,kParserTypeMapInfoDesc,sizeof(tMapInfo)); + LoadEnvironment(gInterfaceGInfo.environment); + gInterfaceGInfo.network=false; + gReplay=true; + gGameInfo=&gInterfaceGInfo; + gReplayAutoCam=true; + if(!(gGameInfo->numLaps==-1&&gBestLapTime==0)) + gCurrentLapStart=0; + + StartReplay(); + gReplayViewedEntityID=0; +/* gFrameCount=0; + gGraphFrameCount=0; + gStartTime=TimeGetSeconds();*/ +} + +int SelectRaceMode(tFileRef map,int (*TimerCallback)(void*),void *userData) +{ + tMapInfo *mapInfo=(tMapInfo*)FileGetParsedDataPtr(map,kParserTypeMapInfoDesc,sizeof(tMapInfo)); + tInterfaceMenuDescribtion menu; + InterfaceInitMenu(&menu,5,"Select Gameplay Mode"); + menu.RenderCallback=InterfaceRenderReplay; + menu.initialSelection=gConfig->arcade; + strcpy(menu.items[0].label,"Simulation"); + strcpy(menu.items[0].describtion,"Simulation Mode tries to resemble real-life car physics as close as possible. Different cars may handle completely different depending on their mass distribution, drivetrain setup and many other factors."); + strcpy(menu.items[1].label,"Arcade"); + strcpy(menu.items[1].describtion,"Arcade Mode puts emphasis on playability and easy handling of cars, but is vastly inaccurate compared to real-life physics. Different cars handle quite similar and differ only in their acceleration and top speed characteristics."); + strcpy(menu.items[2].label,"Turbo Arcade"); + strcpy(menu.items[2].describtion,"Arcade Mode on steroids."); + strcpy(menu.items[3].label,"Strict"); + strcpy(menu.items[3].describtion,"Strict mode is like Simulation mode with modified collision physics to encourage clean racing."); + strcpy(menu.items[menu.numItems-1].label,"Cancel"); + menu.items[menu.numItems-2].lineSpacing*=1.5; + menu.imageXScale=kMapImageXStretch; + menu.imageYScale=kMapImageYStretch; + menu.imageXPos+=0.1; + menu.imageYPos-=0.03; + menu.TimerCallback=TimerCallback; + menu.userData=userData; + menu.image=mapInfo->image; + menu.descPos=Vector(-0.9,-0.6); + int sel=InterfaceGetUserMenuSelection(&menu); + InterfaceDisposeMenu(&menu); + return sel==menu.numItems-1||sel==kInterfaceMenuEsc?-1:sel; +} + +void InterfaceQuickRace() +{ + tGameInfo gInfo; + InitGInfo(&gInfo); + //let user select a map... + if(InterfaceMapSelection(&gInfo,NULL,NULL,false)) + { + int raceMode=SelectRaceMode(gInfo.map,NULL,NULL); + if(raceMode==-1) + return; + else + gConfig->arcade=raceMode; + gInfo.arcade=gConfig->arcade; + tFileRef playerCar=gConfig->lastCar; + UInt8 playerColor=gConfig->lastColor; + //...and a car... + if(InterfaceCarSelection(&playerCar,kCarSelectionQuickMode,&playerColor,NULL,NULL,NULL)) + { + gConfig->lastCar=playerCar; + gConfig->lastColor=playerColor; + + tMapInfo *mapInfo=(tMapInfo*)FileGetParsedDataPtr(gInfo.map,kParserTypeMapInfoDesc,sizeof(tMapInfo)); + int numPlayers=mapInfo->maxPlayers; + //...and opponets + if(InterfaceSelectOpponentCars(&numPlayers,gInfo.playerCars+1,gInfo.playerColors+1,NULL,NULL)) + { + gInfo.numPlayers=numPlayers+1; + gInfo.playerID=0; + gInfo.playerCars[gInfo.playerID]=playerCar; + gInfo.playerColors[gInfo.playerID]=playerColor; + //and run the game + RunGame(&gInfo); + TeaserScreen(); + } + } + } +} + +void InterfaceTimeTrial() +{ + tGameInfo gInfo; + InitGInfo(&gInfo); + gInfo.numPlayers=1; + gInfo.numLaps=-1; + + //let user select a map... + if(InterfaceMapSelection(&gInfo,NULL,NULL,true)) + { + int raceMode=SelectRaceMode(gInfo.map,NULL,NULL); + if(raceMode==-1) + return; + else + gConfig->arcade=raceMode; + gInfo.arcade=gConfig->arcade; + //...and a car... + gInfo.playerCars[0]=gConfig->lastCar; + gInfo.playerColors[0]=gConfig->lastColor; + if(InterfaceCarSelection(gInfo.playerCars,kCarSelectionQuickMode,gInfo.playerColors,NULL,NULL,NULL)) + { + gConfig->lastCar=gInfo.playerCars[0]; + gConfig->lastColor=gInfo.playerColors[0]; + gLocalRecord=0; + + for(int i=0;inumPersonalRecords;i++) + if(gConfig->records[i].map==gInfo.map&&gConfig->records[i].car==gInfo.playerCars[0] + &&gConfig->records[i].mode==gInfo.arcade&&gConfig->records[i].direction==gInfo.reverse) + gLocalRecord=gConfig->records[i].time; + + RunGame(&gInfo); + WriteOutFile(FileGetReference(kConfigFileName),gConfig,kParserTypeConfigDesc); + TeaserScreen(); + } + } +} + +void InterfaceOptions(); + +enum{ + kButtonQuickRace, + kButtonTimeTrial, + kButtonMultiplayer, + kButtonCareer, + kButtonOptions, + kButtonAbout, + kButtonQuit, + kNumButtons +}; + +void InterfaceDisplayRegisterName(void *userData,int selection) +{ +// TextPrintfToBufferFormated(Vector(1.0,-0.9),0.03,kTextAlignRight,"%s%s",RT3_IsRegistered()?"Registered to: ":"",RT3_GetDisplayName()); + TextPrintfToBufferFormated(Vector(1.0,0.95),0.03,kTextAlignRight,kVersionString); + +// TextPrintfToBufferFormated(Vector(-1.0,-0.9),0.03,kTextAlignLeft,kVersionString); +} + +#define kTopSize 0.141 +void InterfaceRenderReplay(void *userData,int selection,void *menu) +{ + float *t,v=0; + static int inited=false; + if(userData) + t=((float*)userData); + else + t=&v; + + if(gConfig->showReplays)//&&0) + { + gBackgroundReplay=true; + if(!inited) + { + gEnableTextureLoad=false; + gNumTexturesRequested=0; + gClipEnable=false; + RenderFrame(false); + gClipEnable=true; + gNumTexturesLoaded=0; + gEnableTextureLoad=true; + gDisabledRestart=0; + + for(int i=0;ibackground!=kFileErr||!gConfig->showReplays) + { + if(((tInterfaceMenuDescribtion*)menu)->background!=kFileErr) + TexturesSelectTex(((tInterfaceMenuDescribtion*)menu)->background); + else + TexturesSelectTex(FileGetReference("background.tif")); + + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); + + if(!gConfig->showReplays) + glDisable(GL_BLEND); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); /*glColor4f(1,1,1,0);*/ glVertex2f(kBackgroundXStretch,kBackgroundYStretch-2*kBackgroundXStretch); + glTexCoord2d(1,0); /*glColor4f(1,1,1,0);*/ glVertex2f(kBackgroundXStretch,kBackgroundYStretch); + glTexCoord2d(0,1); /*glColor4f(1,1,1,1);*/ glVertex2f(-kBackgroundXStretch,kBackgroundYStretch-2*kBackgroundXStretch); + glTexCoord2d(0,0); /*glColor4f(1,1,1,1);*/ glVertex2f(-kBackgroundXStretch,kBackgroundYStretch); + glEnd(); + glEnable(GL_BLEND); + } + + if(((tInterfaceMenuDescribtion*)menu)->image!=kFileErr) + { + TexturesSelectTex(((tInterfaceMenuDescribtion*)menu)->image); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); + + glColor4f(1,1,1,((tInterfaceMenuDescribtion*)menu)->imageAlpha); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex2f(((tInterfaceMenuDescribtion*)menu)->imageXPos+((tInterfaceMenuDescribtion*)menu)->imageXScale,((tInterfaceMenuDescribtion*)menu)->imageYPos-((tInterfaceMenuDescribtion*)menu)->imageYScale); + glTexCoord2d(1,0); glVertex2f(((tInterfaceMenuDescribtion*)menu)->imageXPos+((tInterfaceMenuDescribtion*)menu)->imageXScale,((tInterfaceMenuDescribtion*)menu)->imageYPos+((tInterfaceMenuDescribtion*)menu)->imageYScale); + glTexCoord2d(0,1); glVertex2f(((tInterfaceMenuDescribtion*)menu)->imageXPos-((tInterfaceMenuDescribtion*)menu)->imageXScale,((tInterfaceMenuDescribtion*)menu)->imageYPos-((tInterfaceMenuDescribtion*)menu)->imageYScale); + glTexCoord2d(0,0); glVertex2f(((tInterfaceMenuDescribtion*)menu)->imageXPos-((tInterfaceMenuDescribtion*)menu)->imageXScale,((tInterfaceMenuDescribtion*)menu)->imageYPos+((tInterfaceMenuDescribtion*)menu)->imageYScale); + glEnd(); + glColor4f(1,1,1,1); + } + + TexturesSelectTex(FileGetReference("menustripe.tif")); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex2f((2)*kScreenXPos, 0.7*kScreenYPos); + glTexCoord2d(1,0); glVertex2f((2)*kScreenXPos, 1.0*kScreenYPos); + glTexCoord2d(0,1); glVertex2f((-2)*kScreenXPos,0.7*kScreenYPos); + glTexCoord2d(0,0); glVertex2f((-2)*kScreenXPos,1.0*kScreenYPos); + glEnd(); + + glDisable(GL_TEXTURE_2D); + glColor4f(0,0,0,1); + + glBegin(GL_TRIANGLE_STRIP); + glVertex2f(-kBackgroundXStretch,kBackgroundYStretch-2*kBackgroundXStretch); + glVertex2f(-kBackgroundXStretch,kBackgroundYStretch); + glVertex2f(-2*kBackgroundXStretch,kBackgroundYStretch-2*kBackgroundXStretch); + glVertex2f(-2*kBackgroundXStretch,kBackgroundYStretch); + glEnd(); + + glBegin(GL_TRIANGLE_STRIP); + glVertex2f(2*kBackgroundXStretch,kBackgroundYStretch-2*kBackgroundXStretch); + glVertex2f(2*kBackgroundXStretch,kBackgroundYStretch); + glVertex2f(kBackgroundXStretch,kBackgroundYStretch-2*kBackgroundXStretch); + glVertex2f(kBackgroundXStretch,kBackgroundYStretch); + glEnd(); + + glColor4f(1,1,1,1); + glEnable(GL_TEXTURE_2D); + + } + else if(!gConfig->showReplays) + { + glDisable(GL_BLEND); + TexturesSelectTex(FileGetReference("background.tif")); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); /*glColor4f(1,1,1,0);*/ glVertex2f(kBackgroundXStretch,kBackgroundYStretch-2*kBackgroundXStretch); + glTexCoord2d(1,0); /*glColor4f(1,1,1,0);*/ glVertex2f(kBackgroundXStretch,kBackgroundYStretch); + glTexCoord2d(0,1); /*glColor4f(1,1,1,1);*/ glVertex2f(-kBackgroundXStretch,kBackgroundYStretch-2*kBackgroundXStretch); + glTexCoord2d(0,0); /*glColor4f(1,1,1,1);*/ glVertex2f(-kBackgroundXStretch,kBackgroundYStretch); + glEnd(); + glEnable(GL_BLEND); + } + + + if(*t<0) + *t=TimeGetSeconds(); + + float time=TimeGetSeconds(); + float d=1.5-(time-*t)*2; + if(d<0)d=0; + + if(userData&&!isinf(*t)) + { + TexturesSelectTex(FileGetReference("menulogo.tif")); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex2f((0.3-d)*kScreenXPos, 0.7*kScreenYPos); + glTexCoord2d(1,0); glVertex2f((0.3-d)*kScreenXPos, 1.0*kScreenYPos); + glTexCoord2d(0,1); glVertex2f((-0.95-d)*kScreenXPos,0.7*kScreenYPos); + glTexCoord2d(0,0); glVertex2f((-0.95-d)*kScreenXPos,1.0*kScreenYPos); + glEnd(); + + TexturesSelectTex(FileGetReference("logo.pct")); + + glColor4f(1,1,1,d); + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex2f(kBackgroundXStretch,-kBackgroundYStretch); + glTexCoord2d(1,0); glVertex2f(kBackgroundXStretch,kBackgroundYStretch); + glTexCoord2d(0,1); glVertex2f(-kBackgroundXStretch,-kBackgroundYStretch); + glTexCoord2d(0,0); glVertex2f(-kBackgroundXStretch,kBackgroundYStretch); + glEnd(); + + TextPrintfToBufferFormated(Vector(1.0,-0.9),0.03,kTextAlignRight,"%s%s",RT3_IsRegistered()?"Registered to: ":"",RT3_GetDisplayName()); + TextPrintfToBufferFormated(Vector(1.0,0.88),0.03,kTextAlignRight,kVersionString); + } + gTexturesQualityModifier=0; + glPopAttrib(); +} + +int InterfaceConfirmQuit() +{ + tInterfaceMenuDescribtion menu; + InterfaceInitMenu(&menu,2,"Do you really want to quit?"); + + menu.background=kFileErr; + menu.RenderCallback=InterfaceRenderReplay; + + char itemStrings[][32]={"Quit","Cancel"}; + menu.items[0].lineSpacing*=1.3; + for(int i=0;i0) + InterfaceDrawStatusBar("Loading Textures...","Please Wait",gNumTexturesLoaded/(float)gNumTexturesRequested); + TexturesSelectTex(i); + SystemPoll(true); + } + } + + StartBackgroundReplay(); + gFrameCount=gReplayOldFrameCount; + RunReplay(); + SoundSilence(); + FlushKeys(); + } +} + + +//run the applications main loop +void InterfaceMainLoop() +{ + float userData=-1; + tInterfaceMenuDescribtion menu; + InterfaceInitMenu(&menu,kNumButtons+1,""); + + menu.numItems=kNumButtons; + char itemStrings[][32]={"Quick Race","Time Trial","Multiplayer","Challenges","Options","About","Quit",""}; + for(int i=0;i +#include "gametime.h" +#include +#include +#include "controls.h" +#include "text.h" +#include "interface.h" +#include "screen.h" +#include "network.h" +#include "gameinitexit.h" +#include "gamesystem.h" +#include "carphysics.h" +#include "carselection.h" +#include "mapselection.h" +#include "config.h" +#include "interfaceutil.h" +#include "tracker.h" +#include "random.h" +#include "gamesound.h" +#include "error.h" +#include "initexit.h" +#include "environment.h" +#include "localtracker.h" +#include "writeout.h" +#include "interfacemultiplayer.h" +#include "reg_tool_3.h" +#include "rt3_redline.h" +#undef MemoryMove +#include "gamemem.h" + +enum{ + kMultiplayerHostControls, + kMultiplayerAICars, + kMultiplayerGameMode, + kMultiplayerSelectMap, + kMultiplayerSelectCar, + //kMultiplayerMiniGame, + kMultiplayerStartGame, + kMultiplayerExit, + //kMultiplayerChat, + kNumMultiplayerOptions, + kMultiplayerStartGameSignal, + kMultiplayerForcedExit +}; + +UInt64 gLicenses[kMaxPlayers]; +UInt32 gLicenseCopies[kMaxPlayers]; + +typedef struct{ + float t; + tInterfaceMenuDescribtion *menu; + tGameInfo *gInfo; + float lastSendTime; + int playerCar,playerID; + int locked; + UInt8 playerColor; + tChatBuffer cb; +} tMultiplayerMenuData; + +int gConfirmedVersion=-1; +int gPlayerListShowEntry=-1; +char gDisconnectString[256]; +tChatBuffer *gChatBuffer; +int gOldstartable; +int gReady=false; + +void AddNewPlayer(tGameInfo *gInfo,int netID) +{ + //answer with a message telling the player his ID + NetworkSendPacket(kMessageTypeID,&(gInfo->numNetPlayers),sizeof(UInt8),kMessagePriorityHigh,netID); + //and update the gInfo structure + if(gInfo->numNetPlayers) + gInfo->playerCars[gInfo->numNetPlayers]=-1; + gInfo->netID[gInfo->numNetPlayers]=netID; + gInfo->playerInited[gInfo->numNetPlayers]=false; + gInfo->playerVersions[gInfo->numNetPlayers]=0; + gInfo->numNetPlayers++; + if(gInfo->numPlayersnumNetPlayers) + gInfo->numPlayers=gInfo->numNetPlayers; + + gInfo->version++; + gInfo->playerVersions[0]=gInfo->version; + SystemNotify(); +} + +void FixGInfoForLeftPlayers(tGameInfo *gInfo) +{ + int minPlayerLeft=-1; + int playerLeft; + //PrintConsoleString("Fixing GInfo"); + do + { + playerLeft=-1; + + for(int i=1;inumNetPlayers;i++) + if(gInfo->netID[i]==-1) + { + playerLeft=i; + minPlayerLeft=i; + } + + if(playerLeft!=-1) + { + //PrintConsoleString("Player %d left",playerLeft); + //shift down all the player information for players with higher player ids + for(UInt8 i=playerLeft;inumPlayers-1;i++) + { + gInfo->playerCars[i]=gInfo->playerCars[i+1]; + gInfo->netID[i]=gInfo->netID[i+1]; + gInfo->ping[i]=gInfo->ping[i+1]; + gInfo->playerAddOns[i]=gInfo->playerAddOns[i+1]; + gInfo->playerColors[i]=gInfo->playerColors[i+1]; + gInfo->playerVersions[i]=gInfo->playerVersions[i+1]; + gInfo->playerInited[i]=gInfo->playerInited[i+1]; + strcpy(gInfo->playerCarNames[i],gInfo->playerCarNames[i+1]); + strcpy(gInfo->playerNames[i],gInfo->playerNames[i+1]); + gLicenses[i]=gLicenses[i+1]; + gLicenseCopies[i]=gLicenseCopies[i+1]; + } + for(UInt8 i=gInfo->numPlayers-1;iplayerCars[i]=-1; + gInfo->netID[i]=-1; + gInfo->playerCarNames[i][0]='\0'; + gInfo->playerNames[i][0]='\0'; + gInfo->playerInited[i]=false; + } + gInfo->numPlayers--; + gInfo->numNetPlayers--; + } + } + while(playerLeft!=-1); + + if(minPlayerLeft!=-1) + for(UInt8 i=minPlayerLeft;inumNetPlayers;i++) + //send a message to the player telling him/her to update his/her info + { + NetworkSendPacket(kMessageTypeID,&i,sizeof(UInt8),kMessagePriorityHigh,gInfo->netID[i]); + //PrintConsoleString("member %d is now player %d",gInfo->netID[i],i); + } + gInfo->version++; + gInfo->playerVersions[0]=gInfo->version; +} + +//adds the line in str to the chat buffer, and scrolls buffer if necessary +void ChatBufferInsert(tChatMessage *msg,tChatBuffer *cb) +{ + msg->recvTime=TimeGetSeconds(); + if(msg->str[0]!='\0') + { + if(cb->chatBufferLineschatBuffer[cb->chatBufferLines]=*msg; + cb->chatBufferLines++; + } + else //scrolling is needed + { + for(int i=0;ichatBuffer[i]=cb->chatBuffer[i+1]; + cb->chatBuffer[kChatBufferMaxLines-1]=*msg; + } + } +} + +void VerifyLicenseCopies(tGameInfo *gInfo) +{ + for(int i=1;inumNetPlayers;i++) + if(gInfo->playerInited[i]) + if(gLicenses[i]) + { + int numLicensesUsed=1; + for(int j=0;jgLicenseCopies[i]) + { + NetworkDisconnectPlayer(gInfo->netID[i],kNetworkDisconnectLicenseCopies); + tChatMessage m; + memset((void*)&m,0,sizeof(tChatMessage)); + m.flags=kChatFlagSystem; + sprintf(m.str,"### %s's license code is used by too many players.",gInfo->playerNames[i]); + NetworkSendPacket(kMessageTypeChat,&m,sizeof(tChatMessage),kMessagePriorityHigh,kMessageSendToAll); + } + int valid=true; + int invalid=false; + if(!invalid)qRT3_LicenseIsSameCode(gLicenses[i],RT3_PIRATED_CODE_01,invalid); + if(!invalid)qRT3_LicenseIsSameCode(gLicenses[i],RT3_PIRATED_CODE_02,invalid); + if(!invalid)qRT3_LicenseIsSameCode(gLicenses[i],RT3_PIRATED_FAKE_03,invalid); + if(!invalid)qRT3_LicenseIsSameCode(gLicenses[i],RT3_PIRATED_FAKE_04,invalid); + if(!invalid)qRT3_LicenseIsSameCode(gLicenses[i],RT3_PIRATED_FAKE_05,invalid); + if(!invalid)qRT3_LicenseIsSameCode(gLicenses[i],RT3_PIRATED_FAKE_06,invalid); + if(!invalid)qRT3_LicenseIsSameCode(gLicenses[i],RT3_PIRATED_FAKE_07,invalid); + if(!invalid)qRT3_LicenseIsSameCode(gLicenses[i],RT3_PIRATED_FAKE_08,invalid); + if(!invalid)qRT3_LicenseIsSameCode(gLicenses[i],RT3_PIRATED_FAKE_09,invalid); + if(!invalid)qRT3_LicenseIsSameCode(gLicenses[i],RT3_PIRATED_FAKE_10,invalid); + if(!invalid)qRT3_LicenseIsSameCode(gLicenses[i],RT3_PIRATED_FAKE_11,invalid); + if(!invalid)qRT3_LicenseIsSameCode(gLicenses[i],RT3_PIRATED_FAKE_12,invalid); + if(!invalid)qRT3_LicenseIsSameCode(gLicenses[i],RT3_PIRATED_FAKE_13,invalid); + if(!invalid)qRT3_LicenseIsSameCode(gLicenses[i],RT3_PIRATED_FAKE_14,invalid); + if(!invalid)qRT3_LicenseIsSameCode(gLicenses[i],RT3_PIRATED_FAKE_15,invalid); + if(!invalid)qRT3_LicenseIsSameCode(gLicenses[i],RT3_PIRATED_FAKE_16,invalid); + if(!invalid)qRT3_LicenseIsSameCode(gLicenses[i],RT3_PIRATED_FAKE_17,invalid); + if(!invalid)qRT3_LicenseIsSameCode(gLicenses[i],RT3_PIRATED_FAKE_18,invalid); + if(!invalid)qRT3_LicenseIsSameCode(gLicenses[i],RT3_PIRATED_FAKE_19,invalid); + if(!invalid)qRT3_LicenseIsSameCode(gLicenses[i],RT3_PIRATED_FAKE_20,invalid); + if(!invalid)qRT3_LicenseTestSum(gLicenses[i],valid); + valid=!invalid; + if(!valid) + { + NetworkDisconnectPlayer(gInfo->netID[i],kNetworkDisconnectPirate); + tChatMessage m; + memset((void*)&m,0,sizeof(tChatMessage)); + m.flags=kChatFlagSystem; + sprintf(m.str,"### %s's license code is invalid.",gInfo->playerNames[i]); + NetworkSendPacket(kMessageTypeChat,&m,sizeof(tChatMessage),kMessagePriorityHigh,kMessageSendToAll); + } + } + else if(gConfig->onlyRegisteredPlayers) + { + NetworkDisconnectPlayer(gInfo->netID[i],kNetworkDisconnectNoDemo); + tChatMessage m; + memset((void*)&m,0,sizeof(tChatMessage)); + m.flags=kChatFlagSystem; + sprintf(m.str,"### %s is not registered.",gInfo->playerNames[i]); + NetworkSendPacket(kMessageTypeChat,&m,sizeof(tChatMessage),kMessagePriorityHigh,kMessageSendToAll); + } +} + +//processes network packets while the host is waiting for players to join +void WaitForPlayers(tGameInfo *gInfo,tChatBuffer *cb) +{ + float timeStamp=TimeGetSeconds(); + + //received any network messages? + tNetworkPacket message; + while(NetworkReceivePacket(&message)) + { + switch(message.what) + { + //has a player joined? + case kMessageTypePlayerJoined: + { + UInt32 reply=kVersion; + U32Swap(reply); + NetworkSendPacket(kMessageTypeVersion,&reply,sizeof(UInt32),kMessagePriorityHigh,message.from); + //PrintConsoleString("Player %d joined, NT#:%d",gInfo->numNetPlayers,message.from); + } + break; + + case kMessageTypeVersion: + U32Swap(*(UInt32*)message.data); + if(*((UInt32*)message.data)>=kMinNetworkCompatibleVersion) + AddNewPlayer(gInfo,message.from); + else + { + UInt8 reason=kJoinDeniedVersion; + NetworkSendPacket(kMessageTypeJoinDenied,&reason,sizeof(UInt8),kMessagePriorityHigh,message.from); + } + break; + + //a player has left + case kMessageTypePlayerLeft: + { + int netID=message.from; + int id=-1; + //find the players id + for(int i=0;inumNetPlayers;i++) + if(gInfo->netID[i]==netID) + id=i; + + if(id!=-1) + { + tChatMessage m; + memset((void*)&m,0,sizeof(tChatMessage)); + m.flags=kChatFlagSystem; + if(gInfo->playerVersions[id]!=-2) + sprintf(m.str,"### %s \255#n\255is disconnected due to network problems.",gInfo->playerNames[id]); + else + sprintf(m.str,"### %s \255#n\255quit the game.",gInfo->playerNames[id]); + SoundReInit(); + PlayInterfaceSound(FileGetReference("systemchat.wav")); + NetworkSendPacket(kMessageTypeChat,&m,sizeof(m),kMessagePriorityHigh,kMessageSendToAll); + + gInfo->netID[id]=-1; + FixGInfoForLeftPlayers(gInfo); + } + } + break; + + case kMessageTypeBye: + { + int netID=message.from; + int id=-1; + //find the players id + for(int i=0;inumNetPlayers;i++) + if(gInfo->netID[i]==netID) + id=i; + + if(id!=-1) + { + gInfo->playerVersions[id]=-2; + /*tChatMessage m; + memset((void*)&m,0,sizeof(tChatMessage)); + m.flags=kChatFlagSystem; + sprintf(m.str,"### %s is quitting.",gInfo->playerNames[id]); + SoundReInit(); + PlayInterfaceSound(FileGetReference("systemchat.wav")); + NetworkSendPacket(kMessageTypeChat,&m,sizeof(m),kMessagePriorityHigh,kMessageSendToAll);*/ + } + } + break; + + //a player has told us which car he wishes to use: + case kMessageTypeCarID: + { + int netID=message.from; + SInt8 id=-1; + //find the players id + for(int i=0;inumNetPlayers;i++) + if(gInfo->netID[i]==netID) + id=i; + + if(id!=-1) + { + if(id!=((tCarIDMessage*)message.data)->playerID) + NetworkSendPacket(kMessageTypeID,&id,sizeof(UInt8),kMessagePriorityHigh,netID); + + //update the gInfo structure + if(_stricmp(gInfo->playerNames[id],((tCarIDMessage*)message.data)->playerName)) + { + strcpy(gInfo->playerNames[id],((tCarIDMessage*)message.data)->playerName); + tChatMessage m; + memset((void*)&m,0,sizeof(tChatMessage)); + m.flags=kChatFlagSystem; + SoundReInit(); + PlayInterfaceSound(FileGetReference("systemchat.wav")); + sprintf(m.str,"### %s \255#n\255joined the game.",gInfo->playerNames[id]); + NetworkSendPacket(kMessageTypeChat,&m,sizeof(m),kMessagePriorityHigh,kMessageSendToAll); + } + + if(gInfo->playerCars[id]!=FileGetReference(((tCarIDMessage*)message.data)->carName)||!gInfo->playerInited[id]) + { + gInfo->playerCars[id]=FileGetReference(((tCarIDMessage*)message.data)->carName); + // gInfo->checksums[((tCarIDMessage*)message.data)->playerID]=((tCarIDMessage*)message.data)->checksum; + // U32Swap(gInfo->checksums[((tCarIDMessage*)message.data)->playerID]); + + strcpy(gInfo->playerCarNames[id],((tCarIDMessage*)message.data)->carName); + gInfo->version++; + gInfo->playerVersions[0]=gInfo->version; + } + gInfo->playerInited[id]=true; + gInfo->playerColors[id]=((tCarIDMessage*)message.data)->color; + + gLicenses[id]=((tCarIDMessage*)message.data)->license; + gLicenseCopies[id]=((tCarIDMessage*)message.data)->licenseCopies; + + U64Swap(gLicenses[id]); + U32Swap(gLicenseCopies[id]); + VerifyLicenseCopies(gInfo); + } + } + break; + + //we have received a chat message + case kMessageTypeChat: + //fill in the chat receive buffer + ChatBufferInsert((tChatMessage*)message.data,cb); + if(((tChatMessage*)message.data)->flags&kChatFlagAlert) + { + SoundReInit(); + int tmp=gConfig->interfaceSounds; + gConfig->interfaceSounds=true; + if(((tChatMessage*)message.data)->flags&(kChatFlagSystem|kChatFlagAlert)) + PlayInterfaceSound(FileGetReference("systemchat.wav")); + else + PlayInterfaceSound(FileGetReference("chat.wav")); + gConfig->interfaceSounds=tmp; + SystemNotify(); + } + else + { + SoundReInit(); + if(((tChatMessage*)message.data)->flags&(kChatFlagSystem|kChatFlagAlert)) + PlayInterfaceSound(FileGetReference("systemchat.wav")); + else + PlayInterfaceSound(FileGetReference("chat.wav")); + } + break; + + case kMessageTypeVersionConfirmed: + S32Swap(((int*)message.data)[0]); + S32Swap(((int*)message.data)[1]); + if(gInfo->playerVersions[((int*)message.data)[1]]<((int*)message.data)[0]) + gInfo->playerVersions[((int*)message.data)[1]]=((int*)message.data)[0]; + break; + + case kMessageTypeVersionDenied: + S32Swap(((int*)message.data)[1]); + gInfo->playerVersions[((int*)message.data)[1]]=-1; + break; + } + NetworkDisposePacket(&message); + } +} + +void ConvertGInfoSwap(tGameInfo *gInfo) +{ + U32Swap(gInfo->version); + for(int i=0;iplayerAddOns[i]); + S32Swap(gInfo->playerVersions[i]); + F32Swap(gInfo->ping[i]); + } +} + +void ConvertGInfoSwap(tNetGameInfo *gInfo) +{ + U32Swap(gInfo->version); + for(int i=0;iplayerAddOns[i]); + S32Swap(gInfo->playerVersions[i]); + F32Swap(gInfo->ping[i]); + } +} + +//Send out Ping requests to all the clients +void GatherPingStats(tGameInfo *gInfo) +{ + for(int i=0;inumNetPlayers;i++) + gInfo->ping[i]=NetworkGetPlayerPing(gInfo->netID[i]); +} + +//tell the other players on the network that player has chosen a new car +void UpdatePlayerCar(tMultiplayerMenuData *userData) +{ + tCarIDMessage message; + message.playerID=userData->playerID; + message.color=userData->playerColor; +// message.checksum=FileGetChecksum(userData->playerCar); +// U32Swap(message.checksum); + + message.license=RT3_GetLicenseCode(); + message.licenseCopies=RT3_GetLicenseCopies(); + U64Swap(message.license); + U32Swap(message.licenseCopies); + + sprintf(message.playerName,"%s%s",RT3_IsRegistered()?"":"\255demo.png\255 ",gConfig->playerName); + strcpy(message.carName,FileGetName(userData->playerCar)); + NetworkSendPacket(kMessageTypeCarID,&message,sizeof(tCarIDMessage),kMessagePriorityHigh,kMessageSendToAllButSelf); +} + +void GameInfoSend(tGameInfo *gInfo) +{ + strcpy(gInfo->environmentName,FileGetName(gInfo->environment)); + strcpy(gInfo->mapName,FileGetName(gInfo->map)); + for(int i=0;inumPlayers;i++) + strcpy(gInfo->playerCarNames[i],FileGetName(gInfo->playerCars[i])); +} + +void GameInfoReceive(tGameInfo *gInfo) +{ + gInfo->environment=FileGetReference(gInfo->environmentName); + gInfo->map=FileGetReference(gInfo->mapName); + for(int i=0;inumPlayers;i++) + gInfo->playerCars[i]=FileGetReference(gInfo->playerCarNames[i]); +} + + + +void CompressGInfoToPacket(tNetGameInfo *gInfoPacket,tGameInfo *gInfoSnd) +{ + GameInfoSend(gInfoSnd); + gInfoPacket->inited=gInfoSnd->inited; + gInfoPacket->numNetPlayers=gInfoSnd->numNetPlayers; + gInfoPacket->numPlayers=gInfoSnd->numPlayers; + gInfoPacket->reverse=gInfoSnd->reverse; + gInfoPacket->numLaps=gInfoSnd->numLaps; + gInfoPacket->network=gInfoSnd->network; + gInfoPacket->arcade=gInfoSnd->arcade; + gInfoPacket->carsOnSpeed=gInfoSnd->carsOnSpeed; + gInfoPacket->demolition=gInfoSnd->demolition; + gInfoPacket->version=gInfoSnd->version; + strcpy(gInfoPacket->environmentName,gInfoSnd->environmentName); + strcpy(gInfoPacket->mapName,gInfoSnd->mapName); + for(int i=0;iplayerColors[i]=gInfoSnd->playerColors[i]; + gInfoPacket->netID[i]=gInfoSnd->netID[i]; + gInfoPacket->playerAddOns[i]=gInfoSnd->playerAddOns[i]; + gInfoPacket->ping[i]=gInfoSnd->ping[i]; + gInfoPacket->playerVersions[i]=gInfoSnd->playerVersions[i]; + strcpy(gInfoPacket->playerCarNames[i],gInfoSnd->playerCarNames[i]); + strcpy(gInfoPacket->playerNames[i],gInfoSnd->playerNames[i]); + } + ConvertGInfoSwap(gInfoPacket); +} + +void ExtractGInfoFromPacket(tGameInfo *gInfoRcv,tNetGameInfo *gInfoPacket) +{ + gInfoRcv->inited=gInfoPacket->inited; + gInfoRcv->numNetPlayers=gInfoPacket->numNetPlayers; + gInfoRcv->numPlayers=gInfoPacket->numPlayers; + gInfoRcv->reverse=gInfoPacket->reverse; + gInfoRcv->numLaps=gInfoPacket->numLaps; + gInfoRcv->network=gInfoPacket->network; + gInfoRcv->arcade=gInfoPacket->arcade; + gInfoRcv->carsOnSpeed=gInfoPacket->carsOnSpeed; + gInfoRcv->demolition=gInfoPacket->demolition; + gInfoRcv->version=gInfoPacket->version; + gInfoRcv->maxTime=0; + gInfoRcv->silverTime=0; + gInfoRcv->goldTime=0; + strcpy(gInfoRcv->environmentName,gInfoPacket->environmentName); + strcpy(gInfoRcv->mapName,gInfoPacket->mapName); + for(int i=0;iplayerColors[i]=gInfoPacket->playerColors[i]; + gInfoRcv->netID[i]=gInfoPacket->netID[i]; + gInfoRcv->playerAddOns[i]=gInfoPacket->playerAddOns[i]; + gInfoRcv->ping[i]=gInfoPacket->ping[i]; + gInfoRcv->playerVersions[i]=gInfoPacket->playerVersions[i]; + strcpy(gInfoRcv->playerCarNames[i],gInfoPacket->playerCarNames[i]); + strcpy(gInfoRcv->playerNames[i],gInfoPacket->playerNames[i]); + } + ConvertGInfoSwap(gInfoRcv); + GameInfoReceive(gInfoRcv); +} + +//Send out a packet telling players to start the game +void StartNetGame(tGameInfo *gInfo) +{ + tNetGameInfo msg; + CompressGInfoToPacket(&msg,gInfo); + NetworkSendPacket(kMessageTypeStart,&msg,sizeof(tNetGameInfo),kMessagePriorityHigh,kMessageSendToAllButSelf); +} + +void CheckGInfoVersion(tMultiplayerMenuData *userData,int playerID) +{ + if(playerID==-1) + return; + int ok=true; + for(int i=0;igInfo->numPlayers;i++) + if(userData->gInfo->playerCars[i]==kFileErr&&(userData->gInfo->playerInited[i]||i==0)) + { + tChatMessage m; + m.flags=kChatFlagSystem; + if(playerID!=i) + sprintf(m.str,"### You don't have the data file for the car used by %s",userData->gInfo->playerNames[i]); + else + sprintf(m.str,"### The other players don't have the data file used by your car."); + SoundReInit(); + PlayInterfaceSound(FileGetReference("systemchat.wav")); + ChatBufferInsert(&m,&userData->cb); + ok=false; + } + else if(playerID==i) + { + tCarDefinition *car=(tCarDefinition*)FileGetParsedDataPtr(userData->gInfo->playerCars[i],kParserTypeCarDesc,sizeof(tCarDefinition)); + if(!car) + ok=false; + else if(!car->demoAvailable&&!RT3_IsRegistered()) + { + tChatMessage m; + m.flags=kChatFlagSystem; + sprintf(m.str,"### You can't play the selected car, because you didn't register Redline."); + SoundReInit(); + PlayInterfaceSound(FileGetReference("systemchat.wav")); + ChatBufferInsert(&m,&userData->cb); + ok=false; + } + } + if(userData->gInfo->environment==kFileErr) + { + tChatMessage m; + m.flags=kChatFlagSystem; + sprintf(m.str,"### You don't have the data file for the weather used."); + SoundReInit(); + PlayInterfaceSound(FileGetReference("systemchat.wav")); + ChatBufferInsert(&m,&userData->cb); + ok=false; + } + if(userData->gInfo->map==kFileErr) + { + tChatMessage m; + m.flags=kChatFlagSystem; + sprintf(m.str,"### You don't have the data file for the track used."); + SoundReInit(); + PlayInterfaceSound(FileGetReference("systemchat.wav")); + ChatBufferInsert(&m,&userData->cb); + ok=false; + } + else + { + tMapInfo *mapInfo=(tMapInfo*)FileGetParsedDataPtr(userData->gInfo->map,kParserTypeMapInfoDesc,sizeof(tMapInfo)); + if(!mapInfo->demoAvailable&&!RT3_IsRegistered()) + { + tChatMessage m; + m.flags=kChatFlagSystem; + sprintf(m.str,"### You can't play the selected track, because you didn't register Redline."); + SoundReInit(); + PlayInterfaceSound(FileGetReference("systemchat.wav")); + ChatBufferInsert(&m,&userData->cb); + ok=false; + } + } + +// int reply[2]; +// reply[0]=gInfo->version; +// reply[1]=playerID; + if(ok) + { +// NetworkSendPacket(kMessageTypeVersionConfirmed,reply,sizeof(int)*2,kMessagePriorityHigh,kMessageSendToHostOnly); + gConfirmedVersion=userData->gInfo->version; + gReady=true; + } + else + { +// NetworkSendPacket(kMessageTypeVersionDenied,reply,sizeof(int)*2,kMessagePriorityHigh,kMessageSendToHostOnly); + gConfirmedVersion=-1; + gReady=false; + } +// lastSendTime=TimeGetSeconds(); +} + +//processes network packets while the client is waiting for the host to give the start signal +//returns true if start signal is recieved, false otherwise. +int WaitForStartSignal(tMultiplayerMenuData *userData,int *exit) +{ + tGameInfo *gInfo=userData->gInfo; + tChatBuffer *cb=&userData->cb; + + //received any network messages? + tNetworkPacket message; + while(NetworkReceivePacket(&message)) + { + switch(message.what) + { + //got the start signal? + case kMessageTypeStart: + ExtractGInfoFromPacket(userData->gInfo,(tNetGameInfo*)message.data); + if(gInfo->netID[userData->playerID]!=NetworkGetLocalNetID()) + for(int i=0;inumNetPlayers;i++) + if(gInfo->netID[i]==NetworkGetLocalNetID()) + { + userData->playerID=i; + //PrintConsoleString("fix: we are now player %d",userData->playerID); + UpdatePlayerCar(userData); + break; + } + NetworkDisposePacket(&message); + *exit=kMultiplayerStartGameSignal; + return true; + + //got an GameInfo structure update? + case kMessageTypeGameInfo: + ExtractGInfoFromPacket(userData->gInfo,(tNetGameInfo*)message.data); + if(gInfo->netID[userData->playerID]!=NetworkGetLocalNetID()) + for(int i=0;inumNetPlayers;i++) + if(gInfo->netID[i]==NetworkGetLocalNetID()) + { + userData->playerID=i; + //PrintConsoleString("fix: we are now player %d",userData->playerID); + UpdatePlayerCar(userData); + break; + } + if(gReady) + CheckGInfoVersion(userData,userData->playerID); + //GatherPingStats(gInfo); + break; + + //got a chat message? + case kMessageTypeChat: + //fill in the chat receive buffer + ChatBufferInsert((tChatMessage*)message.data,cb); + if(((tChatMessage*)message.data)->flags&kChatFlagAlert) + { + SoundReInit(); + int tmp=gConfig->interfaceSounds; + gConfig->interfaceSounds=true; + if(((tChatMessage*)message.data)->flags&(kChatFlagSystem|kChatFlagAlert)) + PlayInterfaceSound(FileGetReference("systemchat.wav")); + else + PlayInterfaceSound(FileGetReference("chat.wav")); + gConfig->interfaceSounds=tmp; + SystemNotify(); + } + else + { + SoundReInit(); + if(((tChatMessage*)message.data)->flags&(kChatFlagSystem|kChatFlagAlert)) + PlayInterfaceSound(FileGetReference("systemchat.wav")); + else + PlayInterfaceSound(FileGetReference("chat.wav")); + } + break; + + //change of our id + case kMessageTypeID: + userData->playerID=*(UInt8*)message.data; + //PrintConsoleString("we are now player %d",userData->playerID); + UpdatePlayerCar(userData); + break; + + //game terminated by host + case kMessageTypeBye: + if(message.from==0) + { + sprintf(gDisconnectString,"%s stopped hosting and quit.",gInfo->playerNames[0]); + *exit=kMultiplayerForcedExit; + NetworkDisposePacket(&message); + return true; + } + return false; + + case kMessageTypeGameTerminated: + *exit=kMultiplayerForcedExit; + NetworkDisposePacket(&message); + return true; + + case kMessageTypeKicked: + switch(*(UInt8*)message.data) + { + case kNetworkDisconnectBan: + sprintf(gDisconnectString,"You have been kicked out of the game by the host."); + break; + case kNetworkDisconnectLicenseCopies: + if(RT3_GetLicenseCopies()>1) + sprintf(gDisconnectString,"Your license code is only valid for %d copies.",RT3_GetLicenseCopies()); + else + sprintf(gDisconnectString,"Your license code is only valid for one copy."); + break; + case kNetworkDisconnectPirate: + sprintf(gDisconnectString,"The host did not accept your license code."); + break; + case kNetworkDisconnectNoDemo: + sprintf(gDisconnectString,"The host does not allow unregistered players."); + break; + default: + sprintf(gDisconnectString,"You were disconnected by the host."); + break; + } + *exit=kMultiplayerForcedExit; + NetworkDisposePacket(&message); + return true; + + } + NetworkDisposePacket(&message); + } + return false; +} + +void MultiplayerMenuUpdate(tMultiplayerMenuData *userData,int host) +{ + tInterfaceMenuDescribtion *menu=userData->menu; + if(userData->gInfo->inited) + if(userData->gInfo->network&kGameInfoNetworkForcePlayerCar) + userData->playerCar=userData->gInfo->playerCars[0]; + /*if(userData->playerCar!=kFileErr) + { + tCarDefinition *car=(tCarDefinition*)FileGetParsedDataPtr(userData->playerCar,kParserTypeCarDesc,sizeof(tCarDefinition)); + sprintf(menu->items[kMultiplayerSelectCar].label,"Car: \255#a\255%s...",car->carName); + } + else + sprintf(menu->items[kMultiplayerSelectCar].label,"Car: \255#a\255Data File Mismatch"); */ + if(userData->gInfo->inited) + { + if(userData->gInfo->map!=-1) + { + tMapInfo *mapInfo=(tMapInfo*)FileGetParsedDataPtr(userData->gInfo->map,kParserTypeMapInfoDesc,sizeof(tMapInfo)); + tEnvironment *env=(tEnvironment*)FileGetParsedDataPtr(userData->gInfo->environment,kParserTypeEnvironmentDesc,sizeof(tEnvironment)); + //sprintf(menu->items[kMultiplayerSelectMap].label,"Map: \255#a\255%s (%s%s, %d Laps)...",mapInfo->name,userData->gInfo->reverse?"Reverse, ":"",env->name,userData->gInfo->numLaps); + + } + /* else + sprintf(menu->items[kMultiplayerSelectMap].label,"Map: \255#a\255Data File Mismatch"); + switch(userData->gInfo->arcade) + { + case 0: sprintf(menu->items[kMultiplayerGameMode].label,"Game Mode: \255#a\255Simulation"); break; + case 1: sprintf(menu->items[kMultiplayerGameMode].label,"Game Mode: \255#a\255Arcade"); break; + case 2: sprintf(menu->items[kMultiplayerGameMode].label,"Game Mode: \255#a\255Turbo Arcade"); break; + }*/ + } + if(!host) + { + if(userData->gInfo->network&kGameInfoNetworkForcePlayerCar) + menu->items[kMultiplayerSelectCar].flags|=kInterfaceMenuItemDisabled; + else + menu->items[kMultiplayerSelectCar].flags&=~kInterfaceMenuItemDisabled; + + if(userData->playerID==-1) + menu->items[kMultiplayerStartGame].flags|=kInterfaceMenuItemDisabled; + else if(gConfirmedVersion!=userData->gInfo->playerVersions[userData->playerID]) + { + //menu->items[kMultiplayerStartGame].flags|=kInterfaceMenuItemDisabled; + float time=TimeGetSeconds(); + if(time-userData->lastSendTime>0.5) + { + int reply[2]; + reply[0]=gConfirmedVersion; + reply[1]=userData->playerID; + S32Swap(reply[0]); + S32Swap(reply[1]); + if(gConfirmedVersion!=-1) + NetworkSendPacket(kMessageTypeVersionConfirmed,reply,sizeof(int)*2,kMessagePriorityHigh,kMessageSendToHostOnly); + else + NetworkSendPacket(kMessageTypeVersionDenied,reply,sizeof(int)*2,kMessagePriorityHigh,kMessageSendToHostOnly); + userData->lastSendTime=time; + } + } + else + menu->items[kMultiplayerStartGame].flags&=~kInterfaceMenuItemDisabled; + + if(userData->playerID!=-1) + { +// int startable=userData->gInfo->playerVersions[userData->playerID]==userData->gInfo->version; + int startable=gReady; + if(!startable) + strcpy(menu->items[kMultiplayerStartGame].label,"Ready to Race: \255#a\255No"); + else + strcpy(menu->items[kMultiplayerStartGame].label,"Ready to Race: \255#a\255Yes"); + if(!startable&&gOldstartable) + menu->items[kMultiplayerStartGame].sel+=3.5; + gOldstartable=startable; + } + } + else + { + int startable=true; +// int compatible=true; + for(int i=1;igInfo->numNetPlayers;i++) + if(userData->gInfo->playerVersions[i]!=userData->gInfo->version) + { + startable=false; +// if(userData->gInfo->playerVersions[i]==-1) +// compatible=false; + } +// if(!compatible) +// strcpy(menu->items[kMultiplayerStartGame].label,"Data Files don't match"); +// else + if(startable&&!gOldstartable) + menu->items[kMultiplayerStartGame].sel+=3.5; + if(!startable) + strcpy(menu->items[kMultiplayerStartGame].label,"Send Ready Notification"); + else + strcpy(menu->items[kMultiplayerStartGame].label,"Start Game"); + gOldstartable=startable; + } +} + + +void FillAIPlayers(tMultiplayerMenuData *userData,tGameInfo *gInfo) +{ + int numCars=gInfo->numNetPlayers+gConfig->numEnemies; + if(numCars>(gConfig->allowHugeGames?kMaxPlayers:6))numCars=(gConfig->allowHugeGames?kMaxPlayers:6); + + int availableCars[kMaxCars]; + int carCount; + GetAvailableCars(availableCars,&carCount,false,false); + + for(int i=gInfo->numNetPlayers;igInfo->network&kGameInfoNetworkForcePlayerCar) + { + tCarDefinition *car=(tCarDefinition*)FileGetParsedDataPtr(userData->playerCar,kParserTypeCarDesc,sizeof(tCarDefinition)); + userData->gInfo->playerCars[i]=userData->playerCar; + userData->gInfo->playerColors[i]=(userData->playerColor+i)%car->numColors; + } + else + { + gInfo->playerCars[i]=gConfig->opponentCars[i-gInfo->numNetPlayers]; + gInfo->playerColors[i]=gConfig->opponentColors[i-gInfo->numNetPlayers]; + if(gInfo->playerCars[i]==-1||gInfo->playerCars[i]==0) + { + gInfo->playerCars[i]=availableCars[0]; + gInfo->playerColors[i]=0; + } + } + gInfo->netID[i]=0; + gInfo->ping[i]=0; + gInfo->playerVersions[i]=0; + strcpy(gInfo->playerNames[i],"AI Driver"); + } + gInfo->numPlayers=numCars; +} + + +int NetworkHostCallback(tMultiplayerMenuData *userData) +{ + int showLonelyMessage; + NetworkAdvertiseIdle(userData->gInfo,&showLonelyMessage,userData->locked,false); + if(showLonelyMessage) + { + tChatMessage m; + m.flags=kChatFlagSystem; + sprintf(m.str,"### Players from the Internet may be unable to join you because of the way your router/network is configured."); + SoundReInit(); + PlayInterfaceSound(FileGetReference("systemchat.wav")); + ChatBufferInsert(&m,&userData->cb); + } + + GameInfoSend(userData->gInfo); + GatherPingStats(userData->gInfo); + float time=TimeGetSeconds(); + if(time-userData->lastSendTime>1.0) + { + userData->lastSendTime=time; + tNetGameInfo msg; + CompressGInfoToPacket(&msg,userData->gInfo); + NetworkSendPacket(kMessageTypeGameInfo,&msg,sizeof(tNetGameInfo),kMessagePriorityHigh,kMessageSendToAllButSelf); + } + + WaitForPlayers(userData->gInfo,&userData->cb); + userData->gInfo->playerCars[0]=userData->playerCar; + tCarDefinition *car=(tCarDefinition*)FileGetParsedDataPtr(userData->playerCar,kParserTypeCarDesc,sizeof(tCarDefinition)); + if(userData->gInfo->network&kGameInfoNetworkForcePlayerCar) + for(int i=1;igInfo->numNetPlayers;i++) + { + userData->gInfo->playerCars[i]=userData->playerCar; + userData->gInfo->playerColors[i]=(userData->playerColor+i)%car->numColors; + } + sprintf(userData->gInfo->playerNames[0],"%s%s",RT3_IsRegistered()?"":"\255demo.png\255 ",gConfig->playerName); + userData->gInfo->playerColors[0]=userData->playerColor; + if(gConfig->cantGoBackwards) + userData->gInfo->network|=kGameInfoNetworkCantGoBackwards; + else + userData->gInfo->network&=~kGameInfoNetworkCantGoBackwards; + + FillAIPlayers(userData,userData->gInfo); + + MultiplayerMenuUpdate(userData,true); + return -1; +} + +int NetworkClientCallback(tMultiplayerMenuData *userData) +{ + int exit=false; + tChatMessage chatReceive; + if(WaitForStartSignal(userData,&exit)) + return exit; +/* if(exit) + return kMultiplayerExit; + else + return kMultiplayerStartGameSignal;*/ + + + if(userData->gInfo->inited) + GameInfoReceive(userData->gInfo); + + MultiplayerMenuUpdate(userData,false); + return -1; +} + +#define kPingGreen 0.1 +#define kPingYellow 0.2 +#define kPingRed 0.35 + +void RenderChatBuffer(tChatBuffer *cb,float xleft,float xright,float y,float textsize,char *prompt) +{ + int chatBufferDrawLines[kChatBufferMaxLines]; + int chatBufferTotalDrawLines=0; + + for(int i=0;ichatBufferLines;i++) + { + char str[256]; + int offset=0; + chatBufferDrawLines[i]=0; + do{ + strcpy(str,cb->chatBuffer[i].str+offset); + int word=true; + while(TextWidth(str,textsize)>(xright-xleft)*kTextXPos) + { + word=str[strlen(str)-1]==' '; + str[strlen(str)-1]='\0'; + } + if(!word) + { + int i=strlen(str)-1; + while(str[i]!=' '&&i>0) + i--; + if(i>0&&i>strlen(str)-15) + str[i]='\0'; + } + offset+=strlen(str); + chatBufferDrawLines[i]++; + chatBufferTotalDrawLines++; + }while(offsetchatBuffer[i].str)); + } + + int drawline=0; + if(chatBufferTotalDrawLines>kChatBufferMaxLines)drawline=kChatBufferMaxLines-chatBufferTotalDrawLines; + float t=TimeGetSeconds(); + + char str[1024]; + for(int i=0;ichatBufferLines;i++) + { + int offset=0; + chatBufferDrawLines[i]=0; + do{ + strcpy(str,cb->chatBuffer[i].str+offset); + int word=true; + while(TextWidth(str,textsize)>(xright-xleft)*kTextXPos) + { + word=str[strlen(str)-1]==' '; + str[strlen(str)-1]='\0'; + } + if(!word) + { + int i=strlen(str)-1; + while(str[i]!=' '&&i>0) + i--; + if(i>0&&i>strlen(str)-15) + str[i]='\0'; + } + float r=cb->chatBuffer[i].flags&kChatFlagSystem?0:1; + float g=cb->chatBuffer[i].flags&kChatFlagAlert?0:1; + float b=cb->chatBuffer[i].flags&(kChatFlagSystem+kChatFlagAlert)?0:1; + if(drawline>=0) + if(offset&&!(cb->chatBuffer[i].flags&(kChatFlagAlert|kChatFlagSystem))) + TextPrintfToBufferFormatedColored(Vector(xleft,y-drawline*0.08),textsize,kTextAlignLeft,r,g,b,(t-cb->chatBuffer[i].recvTime)*4,"\255#a\255%s",str); + else + TextPrintfToBufferFormatedColored(Vector(xleft,y-drawline*0.08),textsize,kTextAlignLeft,r,g,b,(t-cb->chatBuffer[i].recvTime)*4,str); + offset+=strlen(str); + drawline++; + }while(offsetchatBuffer[i].str)); + } + do{ + sprintf(str,"> \255#a\255%s",prompt++); + }while(TextWidth(str,textsize)>(xright-xleft)*kTextXPos&&*prompt!='\0'); + if((int)(TimeGetSeconds()*4)%2) + sprintf(str,"%s_",str); + TextPrintfToBufferFormated(Vector(xleft,y-kChatBufferMaxLines*0.08),textsize,kTextAlignLeft,str); +} + +typedef struct{ + char name[256],sub[256]; + float r,g,b,a,sr,sg,sb,sa; +} tPlayerListRenderEntry; + +void RenderPlayerList(char *title,int numEntries,tPlayerListRenderEntry *entries) +{ + TextPrintfToBufferFormated(Vector(0.3,0.65),0.028,kTextAlignLeft,title); + static float shift=0; + static float lt=0; + static float blockt=0; + if(numEntries>6) + { + float t=TimeGetSeconds(); + if(!gSystemSuspended&&t>blockt+1.5) + { + float shiftSize=numEntries+1; + shift+=t-lt; + while(shift>shiftSize) + shift-=shiftSize; + } + lt=t; + if(gPlayerListShowEntry!=-1) + { + shift=gPlayerListShowEntry; + gPlayerListShowEntry=-1; + blockt=t; + } + } + else + shift=0; + + for(int j=0;j<(numEntries>6?numEntries+8:numEntries);j++) + { + float sh=j-shift; + float offs=0.55-sh*0.09; + if(sh+0.5>=0&&sh-0.5<=6) + { + float a=1; + if(numEntries>6) + { + if(sh<0.5)a=sh+0.5; + if(sh>5.5)a=6.5-sh; + } + if(j%(numEntries+1)!=numEntries) + { + TextPrintfToBufferFormatedColored(Vector(0.3,offs),0.028,kTextAlignLeft+kTextAutoCut,entries[j%(numEntries+1)].r,entries[j%(numEntries+1)].g,entries[j%(numEntries+1)].b,entries[j%(numEntries+1)].a*a,entries[j%(numEntries+1)].name); + TextPrintfToBufferFormatedColored(Vector(0.9,offs-0.05),0.02,kTextAlignRight,entries[j%(numEntries+1)].sr,entries[j%(numEntries+1)].sg,entries[j%(numEntries+1)].sb,entries[j%(numEntries+1)].a*a,entries[j%(numEntries+1)].sub); + } + } + } +} + + +void StripText(char *txt,float size,float l) +{ + int len=strlen(txt); + while(TextWidth(txt,size)>l) + txt[--len]='\0'; +} + +void NetworkRenderCallback(tMultiplayerMenuData *userData,int selection,tInterfaceMenuDescribtion *menu) +{ + InterfaceRenderReplay(NULL,selection,menu); + + //print list of players + if(userData->gInfo->inited) + { + tPlayerListRenderEntry pl[kMaxPlayers]; + for(int i=0;igInfo->numPlayers;i++) + { + if(userData->gInfo->playerCars[i]!=kFileErr) + { + pl[i].a=userData->gInfo->playerVersions[i]==userData->gInfo->version?1:0.5; + if(i>=userData->gInfo->numNetPlayers||i==0)pl[i].a=1; + if(i==userData->playerID) + pl[i].r=pl[i].b=0; + tCarDefinition *car=(tCarDefinition*)FileGetParsedDataPtr(userData->gInfo->playerCars[i],kParserTypeCarDesc,sizeof(tCarDefinition)); + int col=userData->gInfo->playerColors[i]; + if(col>=car->numColors) + pl[i].r=pl[i].g=pl[i].b=1; + else + { + pl[i].r=car->colors[col].x; + pl[i].g=car->colors[col].y; + pl[i].b=car->colors[col].z; + } + sprintf(pl[i].name,"\255#w\255%s: \255#n\255%s",userData->gInfo->playerNames[i],car->carName); + } + else{ + pl[i].r=1;pl[i].g=pl[i].b=0; + pl[i].a=0.5; + if(!userData->gInfo->playerInited[i]) + sprintf(pl[i].name,"%s: Joining",userData->gInfo->playerNames[i]); + else + sprintf(pl[i].name,"%s: Data File Mismatch",userData->gInfo->playerNames[i]); + } + if(igInfo->numNetPlayers&&userData->gInfo->ping[i]) + { + if(userData->gInfo->ping[i]gInfo->ping[i]gInfo->ping[i]-kPingGreen)/(kPingYellow-kPingGreen);pl[i].sg=1;pl[i].sb=0;} + else if(userData->gInfo->ping[i]gInfo->ping[i]-kPingYellow)/(kPingRed-kPingYellow);pl[i].sb=0;} + else {pl[i].sr=1;pl[i].sg=0;pl[i].sb=0;} + sprintf(pl[i].sub,"Ping: %3.0fms",userData->gInfo->ping[i]*1000.0); + } + else + strcpy(pl[i].sub,""); + } + + TextPrintfToBufferFormated(Vector(0.3,0.75),0.028,kTextAlignLeft,"Players waiting in Lobby: \255#a\255%d",NetworkCountPlayers()); + char title[256]; + if(userData->gInfo->numPlayers-userData->gInfo->numNetPlayers) + sprintf(title,"Players in game: \255#a\255%d \255#n\255AIs:\255#a\255%d",userData->gInfo->numNetPlayers,userData->gInfo->numPlayers-userData->gInfo->numNetPlayers); + else + sprintf(title,"Players in game: \255#a\255%d",userData->gInfo->numNetPlayers); + RenderPlayerList(title,userData->gInfo->numPlayers,pl); + + if(userData->gInfo->map!=kFileErr) + { + tMapInfo *mapInfo=(tMapInfo*)FileGetParsedDataPtr(userData->gInfo->map,kParserTypeMapInfoDesc,sizeof(tMapInfo)); + if(mapInfo->image) + menu->image=mapInfo->image; + else + menu->image=kFileErr; + menu->imageXScale=0.12; + menu->imageYScale=0.06; + menu->imageXPos=0.01; + menu->imageYPos=0.1; + char str[1024]; + sprintf(str,"Track: \255#a\255%s",mapInfo->name); + StripText(str,0.028,0.26); + TextPrintfToBufferFormated(Vector(-0.22,0.65),0.028,kTextAlignLeft+kTextAutoCut,str); + } + else + { + menu->image=kFileErr; + TextPrintfToBufferFormated(Vector(-0.22,0.65),0.028,kTextAlignLeft+kTextAutoCut,"Track: \255#r\255File not available"); + } + if(userData->gInfo->environment!=kFileErr) + { + tEnvironment *env=(tEnvironment*)FileGetParsedDataPtr(userData->gInfo->environment,kParserTypeEnvironmentDesc,sizeof(tEnvironment)); + if(userData->gInfo->map!=kFileErr) + { + tMapInfo *mapInfo=(tMapInfo*)FileGetParsedDataPtr(userData->gInfo->map,kParserTypeMapInfoDesc,sizeof(tMapInfo)); + if(mapInfo->useAltEnv&&env->hasAltEnv) + env=(tEnvironment*)FileGetParsedDataPtr(env->altEnv,kParserTypeEnvironmentDesc,sizeof(tEnvironment)); + } + TextPrintfToBufferFormated(Vector(-0.22,0.0),0.028,kTextAlignLeft+kTextAutoWrap,"Laps: \255#a\255%d \255#n\255Weather: \255#a\255%s \255#n\255Direction: \255#a\255%s",userData->gInfo->numLaps,env->name,userData->gInfo->reverse?"Reverse":"Normal"); + } + else + TextPrintfToBufferFormated(Vector(-0.22,0.0),0.028,kTextAlignLeft+kTextAutoWrap,"Laps: \255#a\255%d \255#n\255Weather: \255#r\255File not available \255#n\255Direction: \255#a\255%s",userData->gInfo->numLaps,userData->gInfo->reverse?"Reverse":"Normal"); + + + switch(userData->gInfo->arcade) + { + case 0: + TextPrintfToBufferFormated(Vector(-0.22,0.57),0.028,kTextAlignLeft,"Game Mode: \255#a\255Simulation"); + break; + case 1: + TextPrintfToBufferFormated(Vector(-0.22,0.57),0.028,kTextAlignLeft,"Game Mode: \255#a\255Arcade"); + break; + case 2: + TextPrintfToBufferFormated(Vector(-0.22,0.57),0.028,kTextAlignLeft,"Game Mode: \255#a\255Turbo"); + break; + case 3: + TextPrintfToBufferFormated(Vector(-0.22,0.57),0.028,kTextAlignLeft,"Game Mode: \255#a\255Strict"); + break; + } + + + } + else + TextPrintfToBufferFormated(Vector(-0.22,0.57),0.028,kTextAlignLeft,"Waiting for Data..."); + + RenderChatBuffer(&userData->cb,-0.22,0.9,-0.22,0.028,menu->type); +} + +enum{ +// kHostControlAICars, + kHostControlForcePlayerCar, + kHostControlKickPlayer, + kHostControlTrackerName, + kHostControlPassword, + kHostControlOnlyRegistered, + kHostControlCantGoBackwards, + kHostControlLockGame, + kHostControlReturn, + kHostControlNumOptions +}; + + +void MultiplayerKickPlayer(tMultiplayerMenuData *userData) +{ + if(userData->gInfo->numNetPlayers<2) + return; + + tInterfaceMenuDescribtion menu; + InterfaceInitMenu(&menu,userData->gInfo->numNetPlayers,"Select a player to kick:"); + + menu.TimerCallback=(int(*)(void*))NetworkHostCallback; + menu.userData=userData; + + for(int i=1;igInfo->numNetPlayers;i++) + strcpy(menu.items[i-1].label,userData->gInfo->playerNames[i]); + + strcpy(menu.items[userData->gInfo->numNetPlayers-1].label,"Cancel"); + menu.items[userData->gInfo->numNetPlayers-2].lineSpacing*=1.5; + + int sel=InterfaceGetUserMenuSelection(&menu); + InterfaceDisposeMenu(&menu); + + if(sel!=kInterfaceMenuEsc&&sel!=userData->gInfo->numNetPlayers-1) + { + NetworkDisconnectPlayer(userData->gInfo->netID[sel+1],kNetworkDisconnectBan); + tChatMessage m; + memset((void*)&m,0,sizeof(tChatMessage)); + m.flags=kChatFlagSystem; + sprintf(m.str,"### %s was kicked by host.",userData->gInfo->playerNames[sel+1]); + NetworkSendPacket(kMessageTypeChat,&m,sizeof(tChatMessage),kMessagePriorityHigh,kMessageSendToAll); + } +} + +void HostControl(tMultiplayerMenuData *userData) +{ + tInterfaceMenuDescribtion menu; + InterfaceInitMenu(&menu,kHostControlNumOptions,"Host Controls"); + char itemStrings[][32]={"Force host car: ","Kick Player...","Game Name: ","Password: ","Registered players only: ","Allow going backwards:","Lock Game:","Return to chat"}; + for(int i=0;igInfo->numNetPlayers<2) + menu.items[kHostControlKickPlayer].flags|=kInterfaceMenuItemDisabled; + + strcpy(menu.items[kHostControlTrackerName].type,gConfig->gameName); + strcpy(menu.items[kHostControlPassword].type,gConfig->password); + menu.TimerCallback=(int(*)(void*))(NetworkHostCallback); + menu.RenderCallback=InterfaceRenderReplay; + menu.userData=userData; + sprintf(menu.items[kHostControlLockGame].label,"Lock Game: \255#a\255%s",userData->locked?"Yes":"No"); + sprintf(menu.items[kHostControlOnlyRegistered].label,"Registered players only: \255#a\255%s",gConfig->onlyRegisteredPlayers?"Yes":"No"); + sprintf(menu.items[kHostControlForcePlayerCar].label,"Force host car: \255#a\255%s",userData->gInfo->network&kGameInfoNetworkForcePlayerCar?"Yes":"No"); + sprintf(menu.items[kHostControlCantGoBackwards].label,"Allow going backwards: \255#a\255%s",gConfig->cantGoBackwards?"No":"Yes"); + + int exit=false; + InterfaceMenuZoomAnimation(&menu,0,true); + do{ + sprintf(menu.items[kHostControlLockGame].label,"Lock Game: \255#a\255%s",userData->locked?"Yes":"No"); + sprintf(menu.items[kHostControlOnlyRegistered].label,"Registered players only: \255#a\255%s",gConfig->onlyRegisteredPlayers?"Yes":"No"); + sprintf(menu.items[kHostControlCantGoBackwards].label,"Allow going backwards: \255#a\255%s",gConfig->cantGoBackwards?"No":"Yes"); + sprintf(menu.items[kHostControlForcePlayerCar].label,"Force host car: \255#a\255%s",userData->gInfo->network&kGameInfoNetworkForcePlayerCar?"Yes":"No"); + int sel=InterfaceGetUserMenuSelection(&menu); + switch(menu.initialSelection=(sel&kInterfaceMenuItemMask)) + { + case kHostControlKickPlayer: + MultiplayerKickPlayer(userData); + exit=true; + break; + case kHostControlLockGame: + userData->locked=!userData->locked; + break; + case kHostControlOnlyRegistered: + gConfig->onlyRegisteredPlayers=!gConfig->onlyRegisteredPlayers; + break; + case kHostControlCantGoBackwards: + gConfig->cantGoBackwards=!gConfig->cantGoBackwards; + break; + case kHostControlForcePlayerCar: + userData->gInfo->network^=kGameInfoNetworkForcePlayerCar; + userData->gInfo->version++; + userData->gInfo->playerVersions[0]=userData->gInfo->version; + break; + case kHostControlReturn: + case kInterfaceMenuEsc: + exit=true; + break; + } + }while(!exit); + strcpy(gConfig->gameName,menu.items[kHostControlTrackerName].type); + strcpy(gConfig->password,menu.items[kHostControlPassword].type); + NetworkChangePassword(gConfig->password); + NetworkAdvertiseIdle(userData->gInfo,NULL,userData->locked,true); + if(userData->locked) + NetworkLockOutNewPlayers(); + else + NetworkUnlockOutNewPlayers(); + //InterfaceMenuZoomAnimation(&menu,menu.initialSelection,false); + InterfaceDisposeMenu(&menu); +} + +void InitMultiPlayerMenu(tInterfaceMenuDescribtion *menu,int host,tMultiplayerMenuData *userData) +{ + InterfaceInitMenu(menu,kNumMultiplayerOptions,"Multiplayer"); + + char itemStrings[][32]={"Host Controls...","Select AI Cars...","Set Game Mode","Select Track","Select Car","Start Game","Exit Game","Chat Prompt: "}; + for(int i=0;inumItems;i++) + { + strcpy(menu->items[i].label,itemStrings[i]); + menu->items[i].size=0.03;//*=iitems[i].lineSpacing=0.1;//*=inumItems;i++){ + menu->items[i].flags|=kInterfaceMenuItemFixedPos; + menu->items[i].fixedX=-0.9; + menu->items[i].fixedY=-0.74-(i-kMultiplayerStartGame)*0.1; + } + + menu->items[kMultiplayerStartGame-1].lineSpacing*=1.5; + if(!host) + { + menu->items[kMultiplayerSelectMap].flags|=kInterfaceMenuItemDisabled; + menu->items[kMultiplayerHostControls].flags|=kInterfaceMenuItemDisabled; + menu->items[kMultiplayerAICars].flags|=kInterfaceMenuItemDisabled; + menu->items[kMultiplayerGameMode].flags|=kInterfaceMenuItemDisabled; +// menu->items[kMultiplayerMiniGame].flags|=kInterfaceMenuItemDisabled; + menu->items[kMultiplayerStartGame].flags|=kInterfaceMenuItemArrowInput; + } + menu->items[kMultiplayerSelectCar].flags|=kInterfaceMenuItemArrowInput; + menu->items[kMultiplayerSelectMap].flags|=kInterfaceMenuItemArrowInput; + menu->items[kMultiplayerGameMode].flags|=kInterfaceMenuItemArrowInput; +// menu->items[kMultiplayerChat].flags|=kInterfaceMenuItemTypeable; +// menu->items[kMultiplayerChat].maxTypeXPos=0.35; + + menu->background=FileGetReference("background-multiplayer2.tif"); + menu->joinDisable=true; + menu->itemsYPos+=0.1; + //menu->itemsXPos=-0.87; + + menu->menuTypeable=true; + menu->imageXScale=0.12; + menu->imageYScale=0.06; + menu->imageXPos+=0.01; + menu->imageYPos+=0.1; + + menu->initialSelection=kMultiplayerStartGame; + + menu->TimerCallback=(int(*)(void*))(host?NetworkHostCallback:NetworkClientCallback); + menu->RenderCallback=(void(*)(void*,int,void*))NetworkRenderCallback; + menu->userData=userData; + + gOldstartable=host; +} + +void RunMiniGame(tGameInfo *gInfo) +{ + tGameInfo miniGInfo; + InitGInfo(&miniGInfo); + miniGInfo.map=FileGetReference("pit.mapinfo"); + miniGInfo.environment=FileGetReference("sunset.env"); + miniGInfo.demolition=true; + miniGInfo.numPlayers=gInfo->numNetPlayers; + miniGInfo.numNetPlayers=gInfo->numNetPlayers; + miniGInfo.playerID=0; + for(int i=0;inumNetPlayers;i++) + { + miniGInfo.playerCars[i]=FileGetReference("oldford.car"); + strcpy(miniGInfo.playerNames[i],gInfo->playerNames[i]); + } + miniGInfo.network=true; + + tNetGameInfo msg; + CompressGInfoToPacket(&msg,&miniGInfo); + NetworkSendPacket(kMessageTypeGameInfo,&msg,sizeof(tNetGameInfo),kMessagePriorityHigh,kMessageSendToAllButSelf); + + RunGame(&miniGInfo); + + for(int i=1;inetID[i]=-1; + + FixGInfoForLeftPlayers(gInfo); + + CompressGInfoToPacket(&msg,gInfo); + NetworkSendPacket(kMessageTypeGameInfo,&msg,sizeof(tNetGameInfo),kMessagePriorityHigh,kMessageSendToAllButSelf); +} + +int SelectRaceMode(tFileRef map,int (*TimerCallback)(void*),void *userData); + +int HandleMultiplayerMenuItem(tInterfaceMenuDescribtion *menu,tMultiplayerMenuData *userData,int item,int host) +{ + int callbackResponse=-1; + if(*menu->type&&!(item&(kInterfaceMenuLeftArrow|kInterfaceMenuRightArrow))&&(item!=kMultiplayerStartGameSignal)&&(item!=kMultiplayerForcedExit)) + { + tChatMessage m; + memset((void*)&m,0,sizeof(tChatMessage)); + m.flags=0; + sprintf(m.str,"%s:\255#a\255 %s",gConfig->playerName,menu->type); + NetworkSendPacket(kMessageTypeChat,&m,sizeof(tChatMessage),kMessagePriorityHigh,kMessageSendToAll); + *menu->type='\0'; + } + else switch(item&kInterfaceMenuItemMask) + { + case kMultiplayerGameMode: + if(item&kInterfaceMenuLeftArrow){ + userData->gInfo->arcade--; + if(userData->gInfo->arcade<0) + userData->gInfo->arcade=kGameModeStrict;//(item&kInterfaceMenuEasterEgg?2:1); + } + else if(item&kInterfaceMenuRightArrow) + { + userData->gInfo->arcade=(userData->gInfo->arcade+1); + if(userData->gInfo->arcade>kGameModeStrict)//(item&kInterfaceMenuEasterEgg?2:1)) + userData->gInfo->arcade=kGameModeSim; + } + else + { + int arcade=SelectRaceMode(userData->gInfo->map,(int(*)(void*))menu->TimerCallback,userData); + if(arcade==-1) + break; + userData->gInfo->arcade=arcade; + } + userData->gInfo->version++; + userData->gInfo->playerVersions[0]=userData->gInfo->version; + break; + + case kMultiplayerSelectMap: + { + if(item&kInterfaceMenuLeftArrow) + SelectPrevMap(&userData->gInfo->map); + else if(item&kInterfaceMenuRightArrow) + SelectNextMap(&userData->gInfo->map); + else + if(!InterfaceMapSelection(userData->gInfo,(int(*)(void*))menu->TimerCallback,userData,false)) + break; + userData->gInfo->version++; + userData->gInfo->playerVersions[0]=userData->gInfo->version; + } + break; + + case kMultiplayerSelectCar: + { + tFileRef oldCar=userData->playerCar; + if((userData->gInfo->network&kGameInfoNetworkForcePlayerCar&&userData->playerID>0)||userData->playerID==-1) + break; + gPlayerListShowEntry=userData->playerID; + if(item&kInterfaceMenuLeftArrow) + SelectPrevCar(&userData->playerCar,&userData->playerColor,true,true); + else if(item&kInterfaceMenuRightArrow) + SelectNextCar(&userData->playerCar,&userData->playerColor,true,true); + else{ + InterfaceCarSelection(&userData->playerCar,kCarSelectionQuickMode,&userData->playerColor,(int(*)(void*))menu->TimerCallback,userData,&callbackResponse); + if(callbackResponse!=-1) + return HandleMultiplayerMenuItem(menu,userData,callbackResponse,host); + } + if(userData->playerID==0&&oldCar!=userData->playerCar) + { + userData->gInfo->version++; + userData->gInfo->playerVersions[0]=userData->gInfo->version; + } + else if(userData->gInfo->playerCars[userData->playerID]!=userData->playerCar||userData->gInfo->playerColors[userData->playerID]!=userData->playerColor) + UpdatePlayerCar(userData); + + gConfig->lastCar=userData->playerCar; + gConfig->lastColor=userData->playerColor; + } + break; + + case kMultiplayerHostControls: + HostControl(userData); + break; + + case kMultiplayerAICars: + tFileRef opponents[11]; + UInt8 colors[11]; + int numOpponents; + InterfaceSelectOpponentCars(&numOpponents,opponents,colors,(int(*)(void*))(NetworkHostCallback),userData); + break; + + case kMultiplayerStartGame: + if(host) + { + int startable=1; + for(int i=0;igInfo->numNetPlayers;i++) + if(userData->gInfo->playerVersions[i]!=userData->gInfo->version) + { + tChatMessage m; + memset((void*)&m,0,sizeof(tChatMessage)); + m.flags=kChatFlagAlert; + strcpy(m.str,"### Please Select Ready, Host wants to start game!!!"); + NetworkSendPacket(kMessageTypeChat,&m,sizeof(m),kMessagePriorityHigh,userData->gInfo->netID[i]); + startable=0; + } + return startable; + } + else if(userData->gInfo->inited&&userData->playerID!=-1) + { + //if(userData->gInfo->playerVersions[userData->playerID]!=userData->gInfo->version) + if(!gReady) + CheckGInfoVersion(userData,userData->playerID); + else + { + int reply[2]; + reply[0]=userData->gInfo->version; + reply[1]=userData->playerID; + S32Swap(reply[0]); + S32Swap(reply[1]); + NetworkSendPacket(kMessageTypeVersionDenied,reply,sizeof(int)*2,kMessagePriorityHigh,kMessageSendToHostOnly); + gConfirmedVersion=-1; + gReady=false; + gOldstartable=false; + } + } + break; + + /*case kMultiplayerMiniGame: + if(host) + RunMiniGame(userData->gInfo); + break;*/ + + case kMultiplayerExit: + case kInterfaceMenuEsc: + NetworkSendPacket(kMessageTypeBye,NULL,0,kMessagePriorityHigh,kMessageSendToAll); + case kMultiplayerForcedExit: + return -1; + case kMultiplayerStartGameSignal: + return 1; + } + return 0; +} + +void MultiplayerMenu(int host,tMultiplayerMenuData *userData) +{ + tInterfaceMenuDescribtion menu; + userData->menu=&menu; + InitMultiPlayerMenu(&menu,host,userData); + int exit=false; + while(!exit) + { + int start=false; + int zoom=true; + do{ + if(zoom) + InterfaceMenuZoomAnimation(&menu,-1,true); + + int item=InterfaceGetUserMenuSelection(&menu); + int response=HandleMultiplayerMenuItem(&menu,userData,item,host); + menu.initialSelection=(item&kInterfaceMenuItemMask); + // if(/*menu.initialSelection!=kMultiplayerChat||*/menu.initialSelection!=kMultiplayerStartGame||item&(kInterfaceMenuLeftArrow|kInterfaceMenuRightArrow)) + zoom=false; + // else + // zoom=true; + + if(response<0) + exit=true; + else if(response>0)start=true; + }while(!exit&&!start); + if(start) + { + if(host) + StartNetGame(userData->gInfo); + else{ + GameInfoReceive(userData->gInfo); + userData->gInfo->playerID=userData->playerID; + } + NetworkLockOutNewPlayers(); + if(host) + NetworkAdvertiseIdle(userData->gInfo,NULL,true,true); + + SystemNotify(); + RunGame(userData->gInfo); + gReady=false; + strcpy(menu.type,gGameChatMessage); + + NetworkUnlockOutNewPlayers(); + if(host) + FixGInfoForLeftPlayers(userData->gInfo); + } + } + InterfaceDisposeMenu(&menu); + gStartIdleTime=TimeGetSeconds(); +} + +//hosts a network game +void InterfaceHostNetworkGame() +{ + NetworkClearPacketQueue(); + + LocalTrackerStartAdvertising(gConfig->gameName); + + tGameInfo gInfo; + HostNetGame(gConfig->maxPlayers,gConfig->password); + + InitGInfo(&gInfo); + + if(gConfig->lastCar==-1||gConfig->lastCar==0) + { + UInt8 color; + SelectNextCar(&gConfig->lastCar,&color,true,true); + gConfig->lastColor=color; + } + gInfo.network=true; + gInfo.playerCars[0]=gConfig->lastCar; + gInfo.playerColors[0]=gConfig->lastColor; + gInfo.map=gConfig->lastRoad; + if(gInfo.map==kFileErr) + SelectNextMap(&gInfo.map); + gInfo.numLaps=gConfig->lastLaps; + gInfo.environment=gConfig->lastEnv; + gInfo.inited=true; + gInfo.arcade=gConfig->arcade; + gLicenses[0]=RT3_GetLicenseCode(); + gLicenseCopies[0]=RT3_GetLicenseCopies(); + + tMultiplayerMenuData userData; + userData.gInfo=&gInfo; + userData.lastSendTime=0; + userData.playerCar=gConfig->lastCar; + userData.playerID=0; + userData.locked=false; + userData.t=INFINITY; + userData.playerColor=gConfig->lastColor; + userData.cb.chatBufferLines=0; + gChatBuffer=&userData.cb; + + MultiplayerMenu(true,&userData); + + NetworkStopAdvertising(); + LocalTrackerStopAdvertising(); + ExitNetGame(); +} + + +enum{ + kHostOptionsTracker, + kHostOptionsTrackerName, + kHostOptionsMaxPlayers, + kHostOptionsPassword, + kHostOptionsOnlyRegistered, + kHostOptionsPlayerName, + kHostOptionsStart, + kHostOptionsCancel, + kNumHostOptions +}; + +void InterfaceSetupHost() +{ + tInterfaceMenuDescribtion menu; + InterfaceInitMenu(&menu,kNumHostOptions,"Host Game"); + + menu.RenderCallback=InterfaceRenderReplay; + strcpy(menu.items[kHostOptionsCancel].label,"Return to previous menu"); + strcpy(menu.items[kHostOptionsStart].label,"Start Hosting"); + strcpy(menu.items[kHostOptionsTrackerName].label,"Game Name: "); + strcpy(menu.items[kHostOptionsPassword].label,"Game Password: "); + strcpy(menu.items[kHostOptionsOnlyRegistered].label,"Registered players only: "); + strcpy(menu.items[kHostOptionsPlayerName].label,"Local Player Name: "); + + char itemDesc[][256]={ + "Advertises the game to other players on the internet tracker.", + "Name for this game that other players will see.", + "Maximum number of players who can join the game.", + "Password required to join the game. Leave empty to allow everyone to join.", + "Turn on this option to allow only registered players in the game.", + "Your player name.", + "Start hosting the game.", + "Return to multiplayer selection menu.", + }; + for(int i=0;igameName); + menu.items[kHostOptionsPassword].flags|=kInterfaceMenuItemTypeable; + menu.items[kHostOptionsPassword].flags|=kInterfaceMenuItemTypeable|kInterfaceMenuItemTypeHidden; + strcpy(menu.items[kHostOptionsPassword].type,gConfig->password); + menu.items[kHostOptionsPlayerName].flags|=kInterfaceMenuItemTypeable; + menu.items[kHostOptionsPlayerName].maxTypeLength=kMaxNameLength-1; + strcpy(menu.items[kHostOptionsPlayerName].type,gConfig->playerName); + menu.items[kHostOptionsOnlyRegistered].flags|=kInterfaceMenuItemArrowInput; + if(!RT3_IsRegistered()) + menu.items[kHostOptionsOnlyRegistered].flags|=kInterfaceMenuItemDisabled; + + int exit=false; + int zoom=false; + menu.initialSelection=kHostOptionsStart; + do{ + sprintf(menu.items[kHostOptionsTracker].label,"List Game on Tracker: \255#a\255%s",gConfig->trackerEnable?"Yes":"No"); + sprintf(menu.items[kHostOptionsMaxPlayers].label,"Max Number of Players: \255#a\255%d",gConfig->maxPlayers); + sprintf(menu.items[kHostOptionsOnlyRegistered].label,"Registered players only: \255#a\255%s",gConfig->onlyRegisteredPlayers?"Yes":"No"); + if(!zoom){ + InterfaceMenuZoomAnimation(&menu,-1,true); + zoom=true; + } + + int sel=InterfaceGetUserMenuSelection(&menu); + switch(menu.initialSelection=(sel&kInterfaceMenuItemMask)) + { + case kHostOptionsTracker: + gConfig->trackerEnable=!gConfig->trackerEnable; + break; + + case kHostOptionsMaxPlayers: + if(sel&kInterfaceMenuLeftArrow){ + if(gConfig->maxPlayers>2) + gConfig->maxPlayers--; + } + else + if(gConfig->maxPlayers<(gConfig->allowHugeGames?kMaxPlayers:6)) + gConfig->maxPlayers++; + break; + + case kHostOptionsOnlyRegistered: + gConfig->onlyRegisteredPlayers=!gConfig->onlyRegisteredPlayers; + break; + + case kHostOptionsPassword: + case kHostOptionsTrackerName: + case kHostOptionsPlayerName: + case kHostOptionsStart: + case kInterfaceMenuOK: + strcpy(gConfig->gameName,menu.items[kHostOptionsTrackerName].type); + strcpy(gConfig->password,menu.items[kHostOptionsPassword].type); + strcpy(gConfig->playerName,menu.items[kHostOptionsPlayerName].type); + InterfaceHostNetworkGame(); + case kHostOptionsCancel: + case kInterfaceMenuEsc: + exit=true; + break; + } + }while(!exit); + WriteOutFile(FileGetReference(kConfigFileName),gConfig,kParserTypeConfigDesc); + InterfaceDisposeMenu(&menu); +} + +//join a network game +void InterfaceJoinNetworkGame(char *address,char *alias) +{ + int err; + char errorString[256]; + + InterfaceDrawStrings("Connecting to Network Game...",address,kFileErr); + if(gConfig->lastCar==-1||gConfig->lastCar==0) + { + UInt8 color; + SelectNextCar(&gConfig->lastCar,&color,true,true); + gConfig->lastColor=color; + } + + //try to join a game at the given address + if(!JoinNetGame(address,errorString)) + if(*alias) + { + if(!JoinNetGame(alias,errorString)) + { + if(*errorString) + InterfaceDisplayMessage(-1,"Unable To Connect.",errorString); + return; + } + } + else + { + if(*errorString) + InterfaceDisplayMessage(-1,"Unable To Connect.",errorString); + return; + } + + NetworkSearchGamesStop(); + + tGameInfo gInfo; + tMultiplayerMenuData userData; + gInfo.inited=false; + userData.gInfo=&gInfo; + userData.lastSendTime=0; + userData.playerColor=gConfig->lastColor; + userData.playerCar=gConfig->lastCar; + userData.playerID=-1; + userData.cb.chatBufferLines=0; + gChatBuffer=&userData.cb; + gConfirmedVersion=0; + gReady=false; + + MultiplayerMenu(false,&userData); + NetworkCountPlayersStop(); +} + +int gLastLocalGameListUpdate=0; + +#define kMaxGames 256 +#define kMaxLobbyPlayers 256 + +typedef struct{ + tInterfaceMenuDescribtion *menu; + tGameListEntry games[kMaxGames]; + int numGames; + tGameListEntry localGames[kMaxGames]; + int numLocalGames; + tGameListPlayerEntry players[kMaxLobbyPlayers]; + int numPlayers; + int inited; + int numGamesInMenu; + tChatBuffer cb; +} tFindGamesMenuData; + +enum{ + kLobbyHostGame, + kLobbyJoinIP, + kLobbyExit, + kNumLobbyMenuItems +}; + +int FindGamesCallback(tFindGamesMenuData *userData) +{ + int updated; + NetworkSearchGames(&updated,userData->games,&userData->numGames,kMaxGames,userData->players,&userData->numPlayers,kMaxLobbyPlayers); + if(gLocalGameListUpdate!=gLastLocalGameListUpdate) + { + updated=true; + userData->numLocalGames=gNumLocalGames; + if(userData->numLocalGames>kMaxGames) + userData->numLocalGames=kMaxGames; + MemoryMove(userData->localGames,gLocalGames,userData->numLocalGames*sizeof(tGameListEntry)); + gLastLocalGameListUpdate=gLocalGameListUpdate; + } + if(updated) + userData->inited=true; + //if(userData->numGames!=userData->numGamesInMenu) + { + if(userData->numGames==0&&userData->numLocalGames==0) + { + if(userData->inited) + strcpy(userData->menu->items[kNumLobbyMenuItems].label,""); + else + strcpy(userData->menu->items[kNumLobbyMenuItems].label,""); + strcpy(userData->menu->items[kNumLobbyMenuItems].describtion,""); + userData->menu->numItems=kNumLobbyMenuItems+1; + userData->menu->items[kNumLobbyMenuItems].flags|=kInterfaceMenuItemDisabled; + } + else + { + for(int i=0;inumLocalGames;i++) + { + sprintf(userData->menu->items[kNumLobbyMenuItems+i].label,"%s \255rendezvous.pct\255",userData->localGames[i].name); + userData->menu->items[kNumLobbyMenuItems+i].flags=0; + } + for(int i=userData->numLocalGames;inumGames+userData->numLocalGames;i++) + { + if(userData->games[i-userData->numLocalGames].loaded) + { + userData->menu->items[kNumLobbyMenuItems+i].flags=0; + if(userData->games[i-userData->numLocalGames].numPlayers>0) + { + sprintf(userData->menu->items[kNumLobbyMenuItems+i].label,"%s (%d/%d) %s",userData->games[i-userData->numLocalGames].name + ,userData->games[i-userData->numLocalGames].numNetPlayers?userData->games[i-userData->numLocalGames].numNetPlayers:userData->games[i-userData->numLocalGames].numPlayers + ,userData->games[i-userData->numLocalGames].maxPlayers + ,userData->games[i-userData->numLocalGames].password?" \255lock.pct\255":""); + if(userData->games[i-userData->numLocalGames].numNetPlayers>=userData->games[i-userData->numLocalGames].maxPlayers) + userData->menu->items[kNumLobbyMenuItems+i].flags|=kInterfaceMenuItemDisabled; + } + else + strcpy(userData->menu->items[kNumLobbyMenuItems+i].label,userData->games[i-userData->numLocalGames].host); + if(userData->games[i-userData->numLocalGames].locked) + userData->menu->items[kNumLobbyMenuItems+i].flags|=kInterfaceMenuItemDisabled; + if(userData->games[i-userData->numLocalGames].onlyRegistered&&!RT3_IsRegistered()) + userData->menu->items[kNumLobbyMenuItems+i].flags|=kInterfaceMenuItemDisabled; + } + else + { + strcpy(userData->menu->items[kNumLobbyMenuItems+i].label,""); + userData->menu->items[kNumLobbyMenuItems+i].flags|=kInterfaceMenuItemDisabled; + } + } + userData->menu->numItems=userData->numGames+userData->numLocalGames+kNumLobbyMenuItems; + } + userData->numGamesInMenu=userData->numGames+userData->numLocalGames; + } + + tChatMessage msg; + int snd=false; + while(TrackerMessageReceive(&msg)) + { + ChatBufferInsert(&msg,&userData->cb); + if(!(msg.flags&kChatFlagSilent)) + snd=true; + } + if(snd) + { + SoundReInit(); + if(msg.flags&(kChatFlagSystem|kChatFlagAlert)) + PlayInterfaceSound(FileGetReference("systemchat.wav")); + else + PlayInterfaceSound(FileGetReference("chat.wav")); + } + + return -1; +} + +void FindGamesRenderCallback(tFindGamesMenuData *userData,int selection,tInterfaceMenuDescribtion *menu) +{ + InterfaceRenderReplay(NULL,selection,menu); + + if(selection-kNumLobbyMenuItems>=0&&selection-kNumLobbyMenuItemsnumGames+userData->numLocalGames) + { + tGameListEntry *game=selection-kNumLobbyMenuItemsnumLocalGames?userData->localGames+selection-kNumLobbyMenuItems:userData->games+selection-kNumLobbyMenuItems-userData->numLocalGames; + if(!game->loaded) + return; + + // TextPrintfToBufferFormated(Vector(0.3,0.65),0.028,kTextAlignLeft,"\255#a\255redline://%s",game->host); + + if(selection-kNumLobbyMenuItems>=userData->numLocalGames) + { + switch(game->arcade) + { + case 0: + TextPrintfToBufferFormated(Vector(-0.22,0.57),0.028,kTextAlignLeft,"Game Mode: \255#a\255Simulation"); + break; + case 1: + TextPrintfToBufferFormated(Vector(-0.22,0.57),0.028,kTextAlignLeft,"Game Mode: \255#a\255Arcade"); + break; + case 2: + TextPrintfToBufferFormated(Vector(-0.22,0.57),0.028,kTextAlignLeft,"Game Mode: \255#a\255Turbo"); + break; + case 3: + TextPrintfToBufferFormated(Vector(-0.22,0.57),0.028,kTextAlignLeft,"Game Mode: \255#a\255Strict"); + break; + } + char str[1024]; + sprintf(str,"Track: \255#a\255%s",game->map); + StripText(str,0.028,0.26); + TextPrintfToBufferFormated(Vector(-0.22,0.65),0.028,kTextAlignLeft+kTextAutoCut,str); + //TextPrintfToBufferFormated(Vector(0.28,0.35),0.028,kTextAlignLeft,"Password Protection: \255#a\255%s",game->password?"On":"Off"); + + // TextPrintfToBufferFormated(Vector(0.3,0.65),0.028,kTextAlignLeft,"Players in Game: \255#a\255%d/%d",game->numPlayers,game->maxPlayers); + + tPlayerListRenderEntry pl[kMaxPlayers]; + for(int j=0;jnumPlayers;j++) + { + sprintf(pl[j].name,"%s: \255#a\255%s",game->players[j].name,game->players[j].car); + strcpy(pl[j].sub,game->players[j].location); + pl[j].r=pl[j].g=pl[j].b=pl[j].a=1; + pl[j].sr=pl[j].sg=pl[j].sb=1; + } + char title[256]; + sprintf(title,"Players in game: \255#a\255%d/%d",game->numNetPlayers,game->maxPlayers); + RenderPlayerList(title,game->numPlayers,pl); + +/* + for(int j=0;jnumPlayers;j++) + { + TextPrintfToBufferFormated(Vector(0.3,0.65-j*0.1),0.028,kTextAlignLeft+kTextAutoCut,"%s: \255#a\255%s",game->players[j].name,game->players[j].car); + TextPrintfToBufferFormated(Vector(0.97,0.6-j*0.1),0.022,kTextAlignRight,game->players[j].location); + } */ + /* else if(game->joinState==kJoinStateOK) + TextPrintfToBufferFormated(Vector(0.28,0.24),0.028,kTextAlignLeft+kTextAutoWrap,"Joining this game shouldn't cause any troubles."); + else if(game->joinState==kJoinStateReggie) + TextPrintfToBufferFormated(Vector(0.28,0.24),0.028,kTextAlignLeft+kTextAutoWrap,"We'll need to pull off some tricks, but joining will probably work.");*/ + + if(game->onlyRegistered&&!RT3_IsRegistered()) + TextPrintfToBufferFormated(Vector(-0.22,0.0),0.028,kTextAlignLeft+kTextAutoWrap,"\255#r\255Game is only accepting registered players."); + else if(game->locked) + TextPrintfToBufferFormated(Vector(-0.22,0.0),0.028,kTextAlignLeft+kTextAutoWrap,"\255#r\255Game is in progress or not accepting new players."); + else if(game->joinState==kJoinStateFail) + TextPrintfToBufferFormated(Vector(-0.22,0.0),0.028,kTextAlignLeft+kTextAutoWrap,"\255#r\255Due to the way the networks are set up, you probably won't be able to join!"); + else if(game->password) + TextPrintfToBufferFormated(Vector(-0.22,0.0),0.028,kTextAlignLeft+kTextAutoWrap,"This game is password protected!"); + + if(game->mapRef!=kFileErr) + { + tMapInfo *mapInfo=(tMapInfo*)FileGetParsedDataPtr(game->mapRef,kParserTypeMapInfoDesc,sizeof(tMapInfo)); + if(mapInfo->image) + menu->image=mapInfo->image; + else + menu->image=kFileErr; + menu->imageXScale=0.12; + menu->imageYScale=0.06; + menu->imageXPos=0.01; + menu->imageYPos=0.1; + } + else + menu->image=kFileErr; + } + else + { + menu->image=kFileErr; + TextPrintfToBufferFormated(Vector(-0.22,0.57),0.028,kTextAlignLeft,"Game Mode: \255#a\255n/a"); + TextPrintfToBufferFormated(Vector(-0.22,0.65),0.028,kTextAlignLeft,"Track: \255#a\255n/a"); + // TextPrintfToBufferFormated(Vector(0.28,0.35),0.028,kTextAlignLeft,"Password Protection: \255#a\255n/a"); + } + } + else + { + menu->image=FileGetReference("ambrosia.pct"); + menu->imageXScale=0.09; + menu->imageYScale=0.12; + menu->imageXPos=0.01; + menu->imageYPos=0.11; + + tPlayerListRenderEntry pl[kMaxLobbyPlayers]; + for(int j=0;jnumPlayers;j++) + { + pl[j].r=pl[j].g=pl[j].b=pl[j].a=1; + pl[j].sr=pl[j].sg=pl[j].sb=1; + if(userData->players[j].startIdleTime!=-1) + pl[j].a*=0.5; + strcpy(pl[j].name,userData->players[j].name); + if(userData->players[j].startIdleTime!=-1) + { + UInt32 idleTime=TimerGetSeconds()-userData->players[j].startIdleTime; + if(idleTime>3600) + sprintf(pl[j].sub,"Idle for %dh %dm",(int)(idleTime/3600),(((int)idleTime)%3600)/60); + else + sprintf(pl[j].sub,"Idle for %d minutes",(int)(idleTime/60)); + } + else + strcpy(pl[j].sub,userData->players[j].location); + } + char title[256]; + sprintf(title,"Players in lobby: \255#a\255%d",userData->numPlayers); + RenderPlayerList(title,userData->numPlayers,pl); + } +// TextPrintfToBufferFormated(Vector(-0.95,0.65),0.028,kTextAlignLeft,"Active Games: %d",userData->numGames); +// TextPrintfToBufferFormated(Vector( 0.22,0.65),0.028,kTextAlignRight,"Players idling on tracker: %d",userData->numPlayers); + + RenderChatBuffer(&userData->cb,-0.22,0.9,-0.22,0.028,menu->type); +} + +void ManuallyJoinGame() +{ + char address[256]=""; + char exitMessage[256]=""; + if(InterfaceGetUserInputString("Enter IP Address:",address,256,false,false)) + InterfaceJoinNetworkGame(address,""); + ExitNetGame(); + NetworkPreSession(); + if(*gDisconnectString) + InterfaceDisplayMessage(kFileErr,"Disconnected",gDisconnectString); + strcpy(gDisconnectString,""); +} + +tFindGamesMenuData gFindGamesUserData; + +void InterfaceMultiplayerLobby() +{ + gFileErrorReporting=false; + tInterfaceMenuDescribtion menu; + InterfaceInitMenu(&menu,kNumLobbyMenuItems+kMaxGames,"Multiplayer Lobby"); + strcpy(menu.items[kLobbyHostGame].label,"Host Game..."); + strcpy(menu.items[kLobbyJoinIP].label,"Enter IP Address..."); + strcpy(menu.items[kLobbyExit].label,"Exit Multiplayer"); + for(int i=0;iplayerName,menu.type); + TrackerMessageSend(msg); + *menu.type='\0'; + } + else switch(menu.initialSelection) + { + /*case kLobbyChat: + if(*menu.items[kLobbyChat].type) + { + char msg[1024]; + sprintf(msg,"%s:\255#a\255 %s",gConfig->playerName,menu.items[kLobbyChat].type); + TrackerMessageSend(msg); + *menu.items[kLobbyChat].type='\0'; + } + break;*/ + case kLobbyHostGame: + NetworkSearchGamesStop(); + //NetworkSearchGamesDone(); + LocalTrackerSearchGamesDone(); + //ExitNetGame(); + InterfaceSetupHost(); + NetworkCountPlayersStop(); + LocalTrackerSearchGamesInit(); + NetworkPreSession(); + break; + case kLobbyJoinIP: + LocalTrackerSearchGamesDone(); + ManuallyJoinGame(); + LocalTrackerSearchGamesInit(); + break; + case kInterfaceMenuEsc: + case kLobbyExit: + exit=true; + break; + default: + { + LocalTrackerSearchGamesDone(); + char exitMessage[256]=""; + char address[256]=""; + strcpy(address, + menu.initialSelection-kNumLobbyMenuItems +#include "gametime.h" +#include +#include +#include "interfaceutil.h" +#include "gamemem.h" +#include "fileio.h" +#include "screen.h" +#include "controls.h" +#include "text.h" +#include "interface.h" +#include "gameinitexit.h" +#include "parser.h" +#include "writeout.h" +#include "textures.h" +#include "config.h" +#include "gamesound.h" +#include "textures.h" +#include "renderframe.h" +#include "music.h" + +#define kNumControlOptions 14 + +void InterfaceControlOptions() +{ + int controlOptions[kNumControlOptions]={kInputGasButton,kInputBrakeButton,kInputSteerLeftButton,kInputSteerRightButton,kInputHandbrakeButton,kInputKickdownButton,kInputCamera,kInputCameraReverse,kInputCameraChangeCar,kInputPause,kInputGearUp,kInputGearDown,kInputHorn,kInputChat}; + + tInterfaceMenuDescribtion menu; + InterfaceInitMenu(&menu,kNumControlOptions+1,"Controls"); + + strcpy(menu.items[kNumControlOptions].label,"Return to previous menu"); + menu.items[kNumControlOptions-1].lineSpacing*=1.5; + menu.background=FileGetReference("background-options.tif"); + menu.RenderCallback=InterfaceRenderReplay; + menu.itemsYPos+=0.1; + + char itemStrings[kNumControlOptions][32]={"Accelerate","Brake","Steer Left","Steer Right","Handbrake","Full Throttle","Change Camera","Look Backwards","Replay Car View","Pause Game","Shift Up","Shift Down","Horn","Type Message"}; + for(int i=0;ikeys[controlOptions[i]].keyID==gConfig->keys[j].keyID&&j!=controlOptions[i]) + keyUsed=true; + for(int j=0;j<=kInputITunesPlay;j++) + if(gConfig->keys[controlOptions[i]].controllerID1==gConfig->keys[j].controllerID1 + &&gConfig->keys[controlOptions[i]].controllerID2==gConfig->keys[j].controllerID2 + &&gConfig->keys[controlOptions[i]].elementID==gConfig->keys[j].elementID + &&j!=controlOptions[i]) + controllerUsed=true; + if(gConfig->keys[controlOptions[i]].controllerID1) + { + if(keyUsed&&controllerUsed) + sprintf(menu.items[i].label,"%s:\255#r\255\t%s\t%s",itemStrings[i],gConfig->keys[controlOptions[i]].identifier,gConfig->keys[controlOptions[i]].controllerIdentifier); + else if(keyUsed) + sprintf(menu.items[i].label,"%s:\255#r\255\t%s\t\255#a\255%s",itemStrings[i],gConfig->keys[controlOptions[i]].identifier,gConfig->keys[controlOptions[i]].controllerIdentifier); + else if(controllerUsed) + sprintf(menu.items[i].label,"%s:\255#a\255\t%s\t\255#r\255%s",itemStrings[i],gConfig->keys[controlOptions[i]].identifier,gConfig->keys[controlOptions[i]].controllerIdentifier); + else + sprintf(menu.items[i].label,"%s:\255#a\255\t%s\t%s",itemStrings[i],gConfig->keys[controlOptions[i]].identifier,gConfig->keys[controlOptions[i]].controllerIdentifier); + } + else if(keyUsed) + sprintf(menu.items[i].label,"%s:\255#r\255\t%s",itemStrings[i],gConfig->keys[controlOptions[i]].identifier); + else + sprintf(menu.items[i].label,"%s:\255#a\255\t%s",itemStrings[i],gConfig->keys[controlOptions[i]].identifier); + } + InterfaceMenuZoomAnimation(&menu,-1,true); + int sel=InterfaceGetUserMenuSelection(&menu); + if(sel!=kInterfaceMenuEsc&&selkeys+controlOptions[sel]); + + int interfaceKeyPressed; + do{ + interfaceKeyPressed=false; + for(int i=kInterfaceKeyUp;itaunts[i/2]); + sprintf(menu.items[i].label,"Taunt %d message: ",i/2+1); + } + else + { + menu.items[i].lineSpacing*=0.65; + sprintf(itemStrings[i/2],"Taunt %d key",i/2+1); + } + } + + int exit=false; + do{ + for(int i=0;ikeys[kInputTaunt1+i/2].keyID==gConfig->keys[j].keyID&&j!=kInputTaunt1+i/2) + keyUsed=true; + for(int j=0;j<=kInputITunesPlay;j++) + if(gConfig->keys[kInputTaunt1+i/2].controllerID1==gConfig->keys[j].controllerID1 + &&gConfig->keys[kInputTaunt1+i/2].controllerID2==gConfig->keys[j].controllerID2 + &&gConfig->keys[kInputTaunt1+i/2].elementID==gConfig->keys[j].elementID + &&j!=kInputTaunt1+i/2) + controllerUsed=true; + if(gConfig->keys[kInputTaunt1+i/2].controllerID1) + { + if(keyUsed&&controllerUsed) + sprintf(menu.items[i].label,"%s:\255#r\255\t%s\t%s",itemStrings[i/2],gConfig->keys[kInputTaunt1+i/2].identifier,gConfig->keys[kInputTaunt1+i/2].controllerIdentifier); + else if(keyUsed) + sprintf(menu.items[i].label,"%s:\255#r\255\t%s\t\255#a\255%s",itemStrings[i/2],gConfig->keys[kInputTaunt1+i/2].identifier,gConfig->keys[kInputTaunt1+i/2].controllerIdentifier); + else if(controllerUsed) + sprintf(menu.items[i].label,"%s:\255#a\255\t%s\t\255#r\255%s",itemStrings[i/2],gConfig->keys[kInputTaunt1+i/2].identifier,gConfig->keys[kInputTaunt1+i/2].controllerIdentifier); + else + sprintf(menu.items[i].label,"%s:\255#a\255\t%s\t%s",itemStrings[i/2],gConfig->keys[kInputTaunt1+i/2].identifier,gConfig->keys[kInputTaunt1+i/2].controllerIdentifier); + } + else if(keyUsed) + sprintf(menu.items[i].label,"%s:\255#r\255\t%s",itemStrings[i/2],gConfig->keys[kInputTaunt1+i/2].identifier); + else + sprintf(menu.items[i].label,"%s:\255#a\255\t%s",itemStrings[i/2],gConfig->keys[kInputTaunt1+i/2].identifier); + } + + InterfaceMenuZoomAnimation(&menu,-1,true); + int sel=InterfaceGetUserMenuSelection(&menu); + if(sel!=kInterfaceMenuEsc&&selkeys+kInputTaunt1+sel/2); + + int interfaceKeyPressed; + do{ + interfaceKeyPressed=false; + for(int i=kInterfaceKeyUp;itaunts[i/2],menu.items[i].type); + + InterfaceDisposeMenu(&menu); +} + +void InterfaceCalibrateAxis() +{ + if(gConfig->axis[kInputSteerAxis].axisControllerID1>0) + { + InterfaceDrawStrings("Calibrate Axis","Move the Steering Axis to its left and right \nmaximum several times and hit Return.",kFileErr); + CalibrateAxis(kInputSteerAxis); + } + if(!gConfig->seperateGasBrake) + if(gConfig->axis[kInputThrottleBrakeAxis].axisControllerID1>0) + { + InterfaceDrawStrings("Calibrate Axis","Move the Gas/Brake Axis to its left and right \nmaximum several times and hit Return.",kFileErr); + CalibrateAxis(kInputThrottleBrakeAxis); + } + FlushKeys(); +} + +enum{ + kAnalougeSteerAxis, + kAnalougeGasSeperateAxes, + kAnalougeInvertGas, + kAnalougeGasAxis, + kAnalougeBrakeAxis, + kAnalougeDisableTCS, + kAnalougeCalibrate, + kAnalougeSteerCenterTolerance, + kAnalougeGasCenterTolerance, + kAnalougeFFB, + kAnalougeFFBIntensity, + kAnalougeExit, + kAnalougeNumOptions +}; + +void InterfaceAnalogueControlOptions() +{ + tInterfaceMenuDescribtion menu; + InterfaceInitMenu(&menu,kAnalougeNumOptions,"Analog Controls"); + menu.background=FileGetReference("background-options.tif"); + menu.RenderCallback=InterfaceRenderReplay; + + + char itemStrings[][32]={"Steering","Seperate Gas/Brake:","Invert Gas/Brake axes","Gas","Brake","Traction Assistance","Calibrate Axis...","Steering Tolerance:","Gas Tolerance:","","FFB Intensity:","Return to previous menu"}; + + for(int i=0;iaxis[kInputSteerAxis].axisControllerID1) + sprintf(menu.items[kAnalougeSteerAxis].label,"%s: \255#a\255%s",itemStrings[kAnalougeSteerAxis],gConfig->axis[kInputSteerAxis].axisIdentifier); + else + sprintf(menu.items[kAnalougeSteerAxis].label,"%s: \255#a\255not assigned",itemStrings[kAnalougeSteerAxis]); + if(gConfig->seperateGasBrake) + { + if(gConfig->axis[kInputThrottleAxis].axisControllerID1) + sprintf(menu.items[kAnalougeGasAxis].label,"%s: \255#a\255%s","Gas",gConfig->axis[kInputThrottleAxis].axisIdentifier); + else + sprintf(menu.items[kAnalougeGasAxis].label,"%s: \255#a\255not assigned","Gas"); + if(gConfig->axis[kInputBrakeAxis].axisControllerID1) + sprintf(menu.items[kAnalougeBrakeAxis].label,"%s: \255#a\255%s","Brake",gConfig->axis[kInputBrakeAxis].axisIdentifier); + else + sprintf(menu.items[kAnalougeBrakeAxis].label,"%s: \255#a\255not assigned","Brake"); + } else { + if(gConfig->axis[kInputThrottleBrakeAxis].axisControllerID1) + sprintf(menu.items[kAnalougeGasAxis].label,"%s: \255#a\255%s","Gas/Brake",gConfig->axis[kInputThrottleBrakeAxis].axisIdentifier); + else + sprintf(menu.items[kAnalougeGasAxis].label,"%s: \255#a\255not assigned","Gas/Brake"); + sprintf(menu.items[kAnalougeBrakeAxis].label,"%s: \255#a\255not applicable","Brake"); + } + + sprintf(menu.items[kAnalougeFFB].label,"Force Feedback: \255#a\255%s",gConfig->ffb?"On":"Off"); + sprintf(menu.items[kAnalougeInvertGas].label,"Invert Gas/Brake axes: \255#a\255%s",gConfig->reverseGas?"On":"Off"); + sprintf(menu.items[kAnalougeDisableTCS].label,"Traction Assistance: \255#a\255%s",gConfig->disableAnalogueTCS?"Off":"On"); + sprintf(menu.items[kAnalougeGasSeperateAxes].label,"Seperate Gas/Brake: \255#a\255%s",gConfig->seperateGasBrake?"On":"Off"); + if(gConfig->seperateGasBrake) + menu.items[kAnalougeBrakeAxis].flags&=~kInterfaceMenuItemDisabled; + else + menu.items[kAnalougeBrakeAxis].flags|=kInterfaceMenuItemDisabled; + + menu.items[kAnalougeFFBIntensity].sliderPos=gConfig->ffbIntensity; + menu.items[kAnalougeSteerCenterTolerance].sliderPos=gConfig->axis[0].deadzone*5.0; + menu.items[kAnalougeGasCenterTolerance].sliderPos=gConfig->axis[1].deadzone*5.0; + + if(gInputHID&&(gConfig->axis[kInputThrottleBrakeAxis].axisControllerID1||gConfig->axis[kInputSteerAxis].axisControllerID1)) + menu.items[kAnalougeCalibrate].flags&=~kInterfaceMenuItemDisabled; + else + menu.items[kAnalougeCalibrate].flags|=kInterfaceMenuItemDisabled; + + int sel=InterfaceGetUserMenuSelection(&menu); + menu.initialSelection=sel&kInterfaceMenuItemMask; + switch(menu.initialSelection) + { + case kAnalougeSteerAxis: + case kAnalougeGasAxis: + case kAnalougeBrakeAxis: + while(GetInterfaceKey(kInterfaceKeyReturn)||GetInterfaceKey(kInterfaceKeyEnter)); + + menu.initialSelection=sel; + char keyString[80]; + switch(menu.initialSelection) + { + case kAnalougeSteerAxis: + sprintf(keyString,"Move the new axis for '%s'","Steering"); + InterfaceDrawStrings("Assign new Axis",keyString,kFileErr); + GetAxis(gConfig->axis+kInputSteerAxis,false); + break; + case kAnalougeGasAxis: + if(!gConfig->seperateGasBrake) + { + sprintf(keyString,"Move the new axis for '%s'","Gas/Brake"); + InterfaceDrawStrings("Assign new Axis",keyString,kFileErr); + GetAxis(gConfig->axis+kInputThrottleBrakeAxis,false); + } + else + { + sprintf(keyString,"Move the new axis for '%s'","Gas"); + InterfaceDrawStrings("Assign new Axis",keyString,kFileErr); + GetAxis(gConfig->axis+kInputThrottleAxis,true); + } + break; + case kAnalougeBrakeAxis: + sprintf(keyString,"Move the new axis for '%s'","Brake"); + InterfaceDrawStrings("Assign new Axis",keyString,kFileErr); + GetAxis(gConfig->axis+kInputBrakeAxis,true); + break; + } + InterfaceMenuZoomAnimation(&menu,-1,true); + + break; + case kAnalougeGasSeperateAxes: + gConfig->seperateGasBrake=!gConfig->seperateGasBrake; + break; + case kAnalougeInvertGas: + gConfig->reverseGas=!gConfig->reverseGas; + break; + case kAnalougeCalibrate: + InterfaceCalibrateAxis(); + InterfaceMenuZoomAnimation(&menu,-1,true); + break; + case kAnalougeFFB: + gConfig->ffb=!gConfig->ffb; + break; + case kAnalougeDisableTCS: + gConfig->disableAnalogueTCS=!gConfig->disableAnalogueTCS; + break; + case kAnalougeFFBIntensity: + if(sel&kInterfaceMenuLeftArrow){ + gConfig->ffbIntensity-=0.05; + if(gConfig->ffbIntensity<0)gConfig->ffbIntensity=0; + } + else{ + gConfig->ffbIntensity+=0.05; + if(gConfig->ffbIntensity>1.0)gConfig->ffbIntensity=1.0; + } + break; + case kAnalougeSteerCenterTolerance: + if(sel&kInterfaceMenuLeftArrow){ + gConfig->axis[0].deadzone-=0.01; + if(gConfig->axis[0].deadzone<0)gConfig->axis[0].deadzone=0; + } + else{ + gConfig->axis[0].deadzone+=0.01; + if(gConfig->axis[0].deadzone>0.2)gConfig->axis[0].deadzone=0.2; + } + break; + case kAnalougeGasCenterTolerance: + if(sel&kInterfaceMenuLeftArrow){ + gConfig->axis[1].deadzone-=0.01; + if(gConfig->axis[1].deadzone<0)gConfig->axis[1].deadzone=0; + } + else{ + gConfig->axis[1].deadzone+=0.01; + if(gConfig->axis[1].deadzone>0.2)gConfig->axis[1].deadzone=0.2; + } + break; + default: + exit=true; + } + }while(!exit); + gConfig->axis[2].deadzone=gConfig->axis[1].deadzone; + gConfig->axis[3].deadzone=gConfig->axis[1].deadzone; + InterfaceDisposeMenu(&menu); +} + +enum{ + kVideoOptionScreenSize, + kVideoOptionFullscreen, + kVideoOptionShadows, + kVideoOptionTextureFilter, + kVideoOptionTextureQuality, +// kVideoOptionClipDistance, + kVideoOptionInteriorDisplay, + kVideoOptionColor32Bit, + kVideoOptionFSAA, + kVideoOptionMotionBlur, + kVideoOptionsShowReplays, + //kVideoOptionsFPS, + kVideoOptionReturn, + kNumVideoOptions +}; + +#define kMaxTextureQuality 9 +#define kMinTextureQuality 0 + +void InterfaceVideoOptions() +{ + int screenReInitRequired=false; + int textureReLoadRequired=false; + int glReInitRequired=false; + + int videoMode=0; + for(int i=0;iscreenXSize==gVideoModes[i].width&&gConfig->screenYSize==gVideoModes[i].height) + videoMode=i; + + tInterfaceMenuDescribtion menu; + InterfaceInitMenu(&menu,kNumVideoOptions,"Video Options"); + menu.background=FileGetReference("background-options.tif"); + menu.RenderCallback=InterfaceRenderReplay; + + char itemStrings[][256]={"Set the display resolution for Redline to use. Larger values means sharper graphics, but may slow the game down.", + "Switch between fullscreen and windowed modes. You can also switch at any time by pressing Command-F.", + "Turn shadows and light cones on or off. If you can't get Redline to run smooth on your machine, try disabling shadows.", + "Changes texture filter. Anisotropic filtering results in the smoothest graphics, Bilinear filtering is the fastest.", + "Changes the texture quality. Higher quality means sharper graphics, but may cause the game to run too slow, if your graphics card doesn't have enough memory.", +// "Changes the visible display range. Higher values let you see farther, but will cause the game to run slower.", + "Turns car interor display on or off. Turning interior display off will let the game run faster.", + "Changes between 16-bit and 32-bit color modes. 32-bit looks much nicer, but may be slower on older graphics cards.", + "Turns on Full screen Anti-Aliasing. Will cause the game to look smoother, but may cause slowdowns.", + "Turns motion blur on or off (Arcade modes only). Enabling this may cause severe slowdowns on older hardware.", + "Play replays of Races behind the menu screens. May slow down menu responsiveness on older systems.", + // "Display a frames-per-second counter in your HUD, to indicate how fast the graphics are rendering.", + "" + }; + for(int i=0;itextureQualitytextureQuality=kMinTextureQuality; + if(gConfig->textureQuality>kMaxTextureQuality) + gConfig->textureQuality=kMaxTextureQuality; + + if(ScreenNoWindow()) + menu.items[kVideoOptionFullscreen].flags|=kInterfaceMenuItemDisabled; + + int exit=false; + int zoom=false; + do{ + sprintf(menu.items[kVideoOptionScreenSize].label,"Screen Size: \255#a\255%dx%d",gVideoModes[videoMode].width,gVideoModes[videoMode].height); + sprintf(menu.items[kVideoOptionFullscreen].label,"Screen Mode: \255#a\255%s",gConfig->fullscreen?"Fullscreen":"Window"); + switch(gConfig->textureFilter){ + case 0: strcpy(menu.items[kVideoOptionTextureFilter].label,"Texture Filter: \255#a\255Bilinear");break; + case 1: strcpy(menu.items[kVideoOptionTextureFilter].label,"Texture Filter: \255#a\255Trilinear");break; + case 2: strcpy(menu.items[kVideoOptionTextureFilter].label,"Texture Filter: \255#a\255Anisotropic");break; + } + switch(gConfig->fsaa){ + case 0: strcpy(menu.items[kVideoOptionFSAA].label,"Anti-Aliasing: \255#a\255Off");break; + case 2: strcpy(menu.items[kVideoOptionFSAA].label,"Anti-Aliasing: \255#a\255Medium");break; + case 4: strcpy(menu.items[kVideoOptionFSAA].label,"Anti-Aliasing: \255#a\255Full");break; + } + sprintf(menu.items[kVideoOptionShadows].label,"Shadows & Lighting: \255#a\255%s",gConfig->stencil?"On":"Off"); + sprintf(menu.items[kVideoOptionMotionBlur].label,"Motion Blur: \255#a\255%s",gConfig->motionBlur?"On":"Off"); + sprintf(menu.items[kVideoOptionInteriorDisplay].label,"Interior Display: \255#a\255%s",gConfig->interiorDisplay?"On":"Off"); + +// menu.items[kVideoOptionClipDistance].sliderPos=(gConfig->clipFarDistance-160)/640.0; + menu.items[kVideoOptionTextureQuality].sliderPos=1-((float)gConfig->textureQuality-kMinTextureQuality)/(kMaxTextureQuality-kMinTextureQuality); + sprintf(menu.items[kVideoOptionColor32Bit].label,"Color Depth: \255#a\255%d",gConfig->color32Bit?32:16); + sprintf(menu.items[kVideoOptionsShowReplays].label,"Play Background Replays: \255#a\255%s",gConfig->showReplays?"On":"Off"); +// sprintf(menu.items[kVideoOptionsFPS].label,"Show FPS counter: \255#a\255%s",gConfig->performanceStats?"On":"Off"); + + if(!zoom){ + InterfaceMenuZoomAnimation(&menu,-1,true); + zoom=true; + } + + int sel=InterfaceGetUserMenuSelection(&menu); + switch(menu.initialSelection=(sel&kInterfaceMenuItemMask)) + { + case kVideoOptionScreenSize: + if(sel&kInterfaceMenuLeftArrow) + videoMode=(videoMode+1)%gVideoNumModes; + else{ + videoMode--; + if(videoMode<0)videoMode=gVideoNumModes-1; + } + screenReInitRequired=true; + break; + case kVideoOptionFullscreen: + gConfig->fullscreen=!gConfig->fullscreen; + screenReInitRequired=true; + break; + case kVideoOptionShadows: + gConfig->stencil=!gConfig->stencil; + break; + case kVideoOptionMotionBlur: + gConfig->motionBlur=!gConfig->motionBlur; + break; + case kVideoOptionTextureQuality: + if(!(sel&kInterfaceMenuLeftArrow)){ + if(gConfig->textureQuality>kMinTextureQuality) + gConfig->textureQuality--; + } + else{ + if(gConfig->textureQualitytextureQuality++; + } + textureReLoadRequired=true; + break; + case kVideoOptionTextureFilter: + if(!(sel&kInterfaceMenuLeftArrow)){ + gConfig->textureFilter++; + if(gConfig->textureFilter>(ScreenSupportsAnisotropicFiltering()?2:1)) + gConfig->textureFilter=0; + } + else{ + gConfig->textureFilter--; + if(gConfig->textureFilter<0) + gConfig->textureFilter=ScreenSupportsAnisotropicFiltering()?2:1; + } + textureReLoadRequired=true; + break; +/* case kVideoOptionClipDistance: + if(sel&kInterfaceMenuLeftArrow){ + if(gConfig->clipFarDistance>160) + gConfig->clipFarDistance-=20; + } + else{ + if(gConfig->clipFarDistance<800) + gConfig->clipFarDistance+=20; + } + glReInitRequired=true; + break;*/ + case kVideoOptionInteriorDisplay: + gConfig->interiorDisplay=!gConfig->interiorDisplay; + break; + case kVideoOptionColor32Bit: + gConfig->color32Bit=!gConfig->color32Bit; + screenReInitRequired=true; + textureReLoadRequired=true; + break; + case kVideoOptionFSAA: + if(sel&kInterfaceMenuLeftArrow){ + gConfig->fsaa-=2; + if(gConfig->fsaa<0)gConfig->fsaa=4; + } + else + gConfig->fsaa=(gConfig->fsaa+2)%6; + screenReInitRequired=true; + break; + case kVideoOptionsShowReplays: + gConfig->showReplays=!gConfig->showReplays; + break; + + // case kVideoOptionsFPS: + // gConfig->performanceStats=!gConfig->performanceStats; + // break; + + case kVideoOptionReturn: + case kInterfaceMenuEsc: + case kInterfaceMenuOK: + exit=true; + break; + } + }while(!exit); + + gConfig->screenXSize=gVideoModes[videoMode].width; + gConfig->screenYSize=gVideoModes[videoMode].height; + + if(textureReLoadRequired) + TexturesUnloadAll(); + if(screenReInitRequired) + ScreenReInit(); + else + { + if(glReInitRequired) + InitGL(); + } + InterfaceDisposeMenu(&menu); +} + +enum{ + kGameplayOptionsMetric, + kGameplayOptionsAutomatic, + kGameplayOptionsCamera, + kGameplayOptionsTransparency, + kGameplayOptionsName, + kGameplayOptionsRegisterLapTimes, + kGameplayOptionsShowPlayerNames, + kGameplayOptionsCornerGuides, + kGameplayOptionsHugeGames, + kGameplayOptionsGhost, + kGameplayOptionsReturn, + kNumGameplayOptionsButtons +}; + +void InterfaceGameplayOptions() +{ + tInterfaceMenuDescribtion menu; + InterfaceInitMenu(&menu,kNumGameplayOptionsButtons,"Gameplay Options"); + + char itemStrings[][256]={"Switches the game to use either metric or imperial units for speed, power, mass, etc.", + "Switches between automatic or manual gear switching.", + "Switches the default camera mode.", + "Changes the transparency of in-game displays.", + "Lets you change your name used in network games and the internet track records.", + "If you enable this option, your lap times in Time Trial and Challenge modes will be submitted to the internet track record database.", + "If you enable this, player names will be displayed above the cars in network games.", + "Show guiding signs informing you about upcoming corners.", + "Allow races with up to 12 cars. \255#r\255A fast computer and a fast network connection (for multiplayer) are needed for a good gaming experience.", + "Show a ghost car in Time Trial mode, indicating your best lap.", + "" + }; + for(int i=0;iplayerName); + + menu.background=FileGetReference("background-options.tif"); + menu.RenderCallback=InterfaceRenderReplay; + + menu.enterAlwaysOK=true; + + for(int i=0;imetricUnits?"Metric":"Imperial"); + sprintf(menu.items[kGameplayOptionsAutomatic].label,"Transmission: \255#a\255%s",gConfig->automatic?"Automatic":"Manual"); + sprintf(menu.items[kGameplayOptionsRegisterLapTimes].label,"Internet Lap Time Registering: \255#a\255%s",gConfig->registerLapTimes?"On":"Off"); + sprintf(menu.items[kGameplayOptionsCornerGuides].label,"Show Corner Guides: \255#a\255%s",gConfig->guideSigns?"On":"Off"); + sprintf(menu.items[kGameplayOptionsHugeGames].label,"Allow big races: \255#a\255%s",gConfig->allowHugeGames?"On":"Off"); + sprintf(menu.items[kGameplayOptionsGhost].label,"Show ghost car: \255#a\255%s",!gConfig->noGhost?"On":"Off"); + sprintf(menu.items[kGameplayOptionsShowPlayerNames].label,"Show Network Player Names: \255#a\255%s",gConfig->showPlayerNames?"On":"Off"); + switch(gConfig->cameraMode) + { + case kCameraChase: + sprintf(menu.items[kGameplayOptionsCamera].label,"Default Camera: \255#a\255Chase");break; + case kCameraChaseClose: + sprintf(menu.items[kGameplayOptionsCamera].label,"Default Camera: \255#a\255Close Chase");break; + case kCameraCockpit: + sprintf(menu.items[kGameplayOptionsCamera].label,"Default Camera: \255#a\255Cockpit");break; + case kCameraCockpitCarHidden: + sprintf(menu.items[kGameplayOptionsCamera].label,"Default Camera: \255#a\255Cockpit");break; + } + menu.items[kGameplayOptionsTransparency].sliderPos=gConfig->hudTransparency*1/0.8; + menu.items[kGameplayOptionsTransparency].sliderTransparency=gConfig->hudTransparency; + + if(!zoom){ + InterfaceMenuZoomAnimation(&menu,-1,true); + zoom=true; + } + + int sel=InterfaceGetUserMenuSelection(&menu); + switch(menu.initialSelection=(sel&kInterfaceMenuItemMask)) + { + case kGameplayOptionsGhost: + gConfig->noGhost=!gConfig->noGhost; + break; + + case kGameplayOptionsHugeGames: + gConfig->allowHugeGames=!gConfig->allowHugeGames; + break; + + case kGameplayOptionsMetric: + gConfig->metricUnits=!gConfig->metricUnits; + break; + + case kGameplayOptionsRegisterLapTimes: + gConfig->registerLapTimes=!gConfig->registerLapTimes; + break; + + case kGameplayOptionsCornerGuides: + gConfig->guideSigns=!gConfig->guideSigns; + break; + + case kGameplayOptionsAutomatic: + gConfig->automatic=!gConfig->automatic; + break; + + case kGameplayOptionsCamera: + if(sel&kInterfaceMenuLeftArrow){ + gConfig->cameraMode--; + if(gConfig->cameraModecameraMode=kCameraCockpitCarHidden; + } + else{ + gConfig->cameraMode++; + if(gConfig->cameraMode>kCameraCockpitCarHidden)gConfig->cameraMode=kCameraChase; + } + break; + + case kGameplayOptionsTransparency: + if(sel&kInterfaceMenuLeftArrow){ + gConfig->hudTransparency-=0.05; + if(gConfig->hudTransparency<0)gConfig->hudTransparency=0; + } + else{ + gConfig->hudTransparency+=0.05; + if(gConfig->hudTransparency>0.8)gConfig->hudTransparency=0.8; + } + break; + + case kGameplayOptionsShowPlayerNames: + gConfig->showPlayerNames=!gConfig->showPlayerNames; + break; + + case kGameplayOptionsReturn: + case kInterfaceMenuEsc: + case kInterfaceMenuOK: + exit=true; + break; + } + }while(!exit); + + strcpy(gConfig->playerName,menu.items[kGameplayOptionsName].type); + + InterfaceDisposeMenu(&menu); +} + +enum{ + kAudioOptionsVolume, + kAudioOptionsInterfaceSounds, + kAudioOptionsInputITunesNext, + kAudioOptionsInputITunesPrev, + kAudioOptionsInputITunesPlay, + kAudioOptionsReturn, + kNumAudioOptionsButtons +}; + +void InterfaceAudioOptions() +{ + tInterfaceMenuDescribtion menu; + InterfaceInitMenu(&menu,kNumAudioOptionsButtons,"Audio Options"); + + char itemStrings[][256]={"Volume:", + "Interface Sounds:", + "iTunes next song key", + "iTunes previous song key", + "iTunes play/pause key", + "Return to previous menu", + "" + }; + for(int i=0;ikeys[kInputITunesNext+i-kAudioOptionsInputITunesNext].controllerID1) + sprintf(menu.items[i].label,"%s:\255#a\255\t%s\t%s",itemStrings[i],gConfig->keys[kInputITunesNext+i-kAudioOptionsInputITunesNext].identifier,gConfig->keys[kInputITunesNext+i-kAudioOptionsInputITunesNext].controllerIdentifier); + else + sprintf(menu.items[i].label,"%s:\255#a\255\t%s",itemStrings[i],gConfig->keys[kInputITunesNext+i-kAudioOptionsInputITunesNext].identifier); + menu.items[kAudioOptionsVolume].sliderPos=gConfig->soundVolume; + + InterfaceMenuZoomAnimation(&menu,-1,true); + do{ + for(int i=kAudioOptionsInputITunesNext;i<=kAudioOptionsInputITunesPlay;i++) + if(gConfig->keys[kInputITunesNext+i-kAudioOptionsInputITunesNext].controllerID1) + sprintf(menu.items[i].label,"%s:\255#a\255\t%s\t%s",itemStrings[i],gConfig->keys[kInputITunesNext+i-kAudioOptionsInputITunesNext].identifier,gConfig->keys[kInputITunesNext+i-kAudioOptionsInputITunesNext].controllerIdentifier); + else + sprintf(menu.items[i].label,"%s:\255#a\255\t%s",itemStrings[i],gConfig->keys[kInputITunesNext+i-kAudioOptionsInputITunesNext].identifier); + + menu.items[kAudioOptionsVolume].sliderPos=gConfig->soundVolume; + sprintf(menu.items[kAudioOptionsInterfaceSounds].label,"Interface Sounds: \255#a\255%s",gConfig->interfaceSounds?"On":"Off"); + + int sel=InterfaceGetUserMenuSelection(&menu); + switch(menu.initialSelection=(sel&kInterfaceMenuItemMask)) + { + case kAudioOptionsVolume: + if(sel&kInterfaceMenuLeftArrow){ + gConfig->soundVolume-=0.1; + if(gConfig->soundVolume<0)gConfig->soundVolume=0; + } + else{ + gConfig->soundVolume+=0.1; + if(gConfig->soundVolume>1)gConfig->soundVolume=1; + } + SoundReInit(); + break; + + case kAudioOptionsInterfaceSounds: + gConfig->interfaceSounds=!gConfig->interfaceSounds; + break; + + case kAudioOptionsInputITunesNext: + case kAudioOptionsInputITunesPrev: + case kAudioOptionsInputITunesPlay: + { + while(GetInterfaceKey(kInterfaceKeyReturn)||GetInterfaceKey(kInterfaceKeyEnter)); + + menu.initialSelection=sel; + char keyString[80]; + sprintf(keyString,"Press the new Key or Button for '%s'\n (Or press delete to clear)",itemStrings[sel]); + InterfaceDrawStrings("Assign new Key",keyString,kFileErr); + + GetInput(gConfig->keys+kInputITunesNext+menu.initialSelection-kAudioOptionsInputITunesNext); + + int interfaceKeyPressed; + do{ + interfaceKeyPressed=false; + for(int i=kInterfaceKeyUp;i +#include +#include +#include +#include "gamemem.h" +#include "textures.h" +#include "gametime.h" +#include "interface.h" +#include "interfaceutil.h" +#include "vectors.h" +#include "text.h" +#include "controls.h" +#include "screen.h" +#include "carphysics.h" +#include "gamesystem.h" +#include "gamesound.h" +#include "random.h" +#include "interfacemultiplayer.h" +#include "log.h" +#include "reg_tool_3.h" +#include "initexit.h" +#include "ASWRegistrationCarbonAPI.h" + +int gJoinFlag=false; +int gInterfaceType=false; +char gJoinHost[256]; +char gLastMenuChar; +float gStartIdleTime=0; + +/* +#define kNumEasterEggStrings 3 +char gEasterEggStrings[kNumEasterEggStrings][256]={ + "Easter Egg not yet implemented...", + "Bla Bla", + "Insert Funny Stuff here" + }; + +/*/ +#define kNumEasterEggStrings 10 +char gEasterEggStrings[kNumEasterEggStrings][256]={ + "You better respect me, 'cos I'm the man with the car with the star", + "It's a long way to the top (if you wanna rock'n'roll!)", + "There's no replacement for displacement", + "echo \"carsOnSpeed 1\" >> ~/Library/Preferences/Redline\ Preferences", + "Moof!", + "All your horsepower are belong to us", + "Frog blast the vent core!", + "\255#w\255Everyone likes \255#r\255c\255#y\255o\255#g\255l\255#c\255o\255#b\255r\255#w\255 tags: \\255#r\\255", + "In Soviet Russia, car drives you!", + "out of errors." + }; + +#define kEasterEggScrollSpeed 0.8 + +#define kMenuFPS 75.0 +#define kMenuFrameTime (1/kMenuFPS) +#define kMenuSuspendedFPS 0.5 +#define kMenuSuspendedFrameTime (1/kMenuSuspendedFPS) + +void InterfaceDrawBackgroundFade(float opacity,int showtop) +{ + gTexturesQualityModifier=-255; + if(opacity==0) + return; + glPushAttrib(GL_DEPTH_BUFFER_BIT+GL_LIGHTING_BIT+GL_COLOR_BUFFER_BIT+GL_TEXTURE_BIT+GL_CURRENT_BIT); + + glLoadIdentity(); + glTranslatef(0.0f,0.0f,-1.0f); + + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + + + glColor4f(1,1,1,opacity); + TexturesSelectTex(FileGetReference(kInterfaceBackgroundTexture)); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); + + if(showtop) + { + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex2f(kBackgroundXStretch,kBackgroundYStretch-2*kBackgroundXStretch); + glTexCoord2d(1,0); glVertex2f(kBackgroundXStretch,kBackgroundYStretch); + glTexCoord2d(0,1); glVertex2f(-kBackgroundXStretch,kBackgroundYStretch-2*kBackgroundXStretch); + glTexCoord2d(0,0); glVertex2f(-kBackgroundXStretch,kBackgroundYStretch); + glEnd(); + + TexturesSelectTex(FileGetReference("menustripe.tif")); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex2f(kBackgroundXStretch, 0.7*kScreenYPos); + glTexCoord2d(1,0); glVertex2f(kBackgroundXStretch, 1.0*kScreenYPos); + glTexCoord2d(0,1); glVertex2f(-kBackgroundXStretch,0.7*kScreenYPos); + glTexCoord2d(0,0); glVertex2f(-kBackgroundXStretch,1.0*kScreenYPos); + glEnd(); + } + else + { + if(ScreenSupportsBlendColor()) + { + glBlendFunc(GL_CONSTANT_ALPHA,GL_ONE_MINUS_CONSTANT_ALPHA); + glBlendColor(1,1,1,opacity); + } + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex2f(kBackgroundXStretch,kBackgroundYStretch-2*kBackgroundXStretch); + glTexCoord2d(1,0.15); glVertex2f(kBackgroundXStretch,kBackgroundYStretch-0.3*kBackgroundXStretch); + glTexCoord2d(0,1); glVertex2f(-kBackgroundXStretch,kBackgroundYStretch-2*kBackgroundXStretch); + glTexCoord2d(0,0.15); glVertex2f(-kBackgroundXStretch,kBackgroundYStretch-0.3*kBackgroundXStretch); + glEnd(); + } + glDisable(GL_TEXTURE_2D); + glColor4f(0,0,0,opacity); + glBegin(GL_TRIANGLE_STRIP); + glVertex2f(-kBackgroundXStretch,kBackgroundYStretch-2*kBackgroundXStretch); + glVertex2f(-kBackgroundXStretch,kBackgroundYStretch); + glVertex2f(-2*kBackgroundXStretch,kBackgroundYStretch-2*kBackgroundXStretch); + glVertex2f(-2*kBackgroundXStretch,kBackgroundYStretch); + glEnd(); + glBegin(GL_TRIANGLE_STRIP); + glVertex2f(2*kBackgroundXStretch,kBackgroundYStretch-2*kBackgroundXStretch); + glVertex2f(2*kBackgroundXStretch,kBackgroundYStretch); + glVertex2f(kBackgroundXStretch,kBackgroundYStretch-2*kBackgroundXStretch); + glVertex2f(kBackgroundXStretch,kBackgroundYStretch); + glEnd(); + glPopAttrib(); + gTexturesQualityModifier=0; +} + +void InterfaceDrawBackground(tFileRef background,tFileRef image,float imageXScale,float imageYScale,float imageXPos,float imageYPos,float alpha) +{ + glLoadIdentity(); + glTranslatef(0.0f,0.0f,-1.0f); + gTexturesQualityModifier=-255; + + glPushAttrib(GL_DEPTH_BUFFER_BIT+GL_LIGHTING_BIT+GL_COLOR_BUFFER_BIT+GL_CURRENT_BIT); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + + glClear(GL_COLOR_BUFFER_BIT); + + if(background!=kFileErr) + TexturesSelectTex(background); + else + TexturesSelectTex(FileGetReference(kInterfaceBackgroundTexture)); + + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); + + +/* + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex2f(kBackgroundXStretch,-kBackgroundYStretch); + glTexCoord2d(1,0); glVertex2f(kBackgroundXStretch,kBackgroundYStretch); + glTexCoord2d(0,1); glVertex2f(-kBackgroundXStretch,-kBackgroundYStretch); + glTexCoord2d(0,0); glVertex2f(-kBackgroundXStretch,kBackgroundYStretch); + glEnd(); +*/ + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex2f(kBackgroundXStretch,kBackgroundYStretch-2*kBackgroundXStretch); + glTexCoord2d(1,0); glVertex2f(kBackgroundXStretch,kBackgroundYStretch); + glTexCoord2d(0,1); glVertex2f(-kBackgroundXStretch,kBackgroundYStretch-2*kBackgroundXStretch); + glTexCoord2d(0,0); glVertex2f(-kBackgroundXStretch,kBackgroundYStretch); + glEnd(); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + + TexturesSelectTex(FileGetReference("menustripe.tif")); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex2f(kBackgroundXStretch, 0.7*kScreenYPos); + glTexCoord2d(1,0); glVertex2f(kBackgroundXStretch, 1.0*kScreenYPos); + glTexCoord2d(0,1); glVertex2f(-kBackgroundXStretch,0.7*kScreenYPos); + glTexCoord2d(0,0); glVertex2f(-kBackgroundXStretch,1.0*kScreenYPos); + glEnd(); + + if(image!=kFileErr) + { + glColor4f(1,1,1,alpha); + TexturesSelectTex(image); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex2f(imageXPos+imageXScale,imageYPos-imageYScale); + glTexCoord2d(1,0); glVertex2f(imageXPos+imageXScale,imageYPos+imageYScale); + glTexCoord2d(0,1); glVertex2f(imageXPos-imageXScale,imageYPos-imageYScale); + glTexCoord2d(0,0); glVertex2f(imageXPos-imageXScale,imageYPos+imageYScale); + glEnd(); + } + + glPopAttrib(); + gTexturesQualityModifier=0; +} + +void InterfaceFadeInImageFromBlack(float xSize,float ySize,tFileRef image,float maxTime) +{ + gTexturesQualityModifier=-255; + TexturesSelectTex(image); + float startTime=TimeGetSeconds(); + float time=0; + int exit=false; + + glLoadIdentity(); + glTranslatef(0.0f,0.0f,-1.0f); + + while(!exit) + { + time=TimeGetSeconds()-startTime; + float alpha=(time/maxTime); + + glPushAttrib(GL_DEPTH_BUFFER_BIT+GL_COLOR_BUFFER_BIT+GL_LIGHTING_BIT+GL_TEXTURE_BIT+GL_CURRENT_BIT); + + glDisable(GL_LIGHTING); + glDisable(GL_DEPTH_TEST); + + glClear(GL_COLOR_BUFFER_BIT); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + glColor4f(1,1,1,alpha); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex2f(kBackgroundXStretch*xSize,-kBackgroundYStretch*ySize); + glTexCoord2d(1,0); glVertex2f(kBackgroundXStretch*xSize,kBackgroundYStretch*ySize); + glTexCoord2d(0,1); glVertex2f(-kBackgroundXStretch*xSize,-kBackgroundYStretch*ySize); + glTexCoord2d(0,0); glVertex2f(-kBackgroundXStretch*xSize,kBackgroundYStretch*ySize); + glEnd(); + + glPopAttrib(); + + ScreenBlit(); + if(time>=maxTime) + exit=true; + } + gTexturesQualityModifier=0; +} + +void InterfaceFadeInImageFromImage(float xSize,float ySize,tFileRef image,float xSize2,float ySize2,tFileRef image2,float maxTime) +{ + gTexturesQualityModifier=-255; + float startTime=TimeGetSeconds(); + float time=0; + int exit=false; + + glLoadIdentity(); + glTranslatef(0.0f,0.0f,-1.0f); + + while(!exit) + { + time=TimeGetSeconds()-startTime; + float alpha=(time/maxTime); + + glPushAttrib(GL_DEPTH_BUFFER_BIT+GL_COLOR_BUFFER_BIT+GL_LIGHTING_BIT+GL_TEXTURE_BIT+GL_CURRENT_BIT); + + glDisable(GL_LIGHTING); + glDisable(GL_DEPTH_TEST); + + glClear(GL_COLOR_BUFFER_BIT); + + TexturesSelectTex(image); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex2f(kBackgroundXStretch*xSize,-kBackgroundYStretch*ySize); + glTexCoord2d(1,0); glVertex2f(kBackgroundXStretch*xSize,kBackgroundYStretch*ySize); + glTexCoord2d(0,1); glVertex2f(-kBackgroundXStretch*xSize,-kBackgroundYStretch*ySize); + glTexCoord2d(0,0); glVertex2f(-kBackgroundXStretch*xSize,kBackgroundYStretch*ySize); + glEnd(); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + glColor4f(1,1,1,alpha); + TexturesSelectTex(image2); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex2f(kBackgroundXStretch*xSize2,-kBackgroundYStretch*ySize2); + glTexCoord2d(1,0); glVertex2f(kBackgroundXStretch*xSize2,kBackgroundYStretch*ySize2); + glTexCoord2d(0,1); glVertex2f(-kBackgroundXStretch*xSize2,-kBackgroundYStretch*ySize2); + glTexCoord2d(0,0); glVertex2f(-kBackgroundXStretch*xSize2,kBackgroundYStretch*ySize2); + glEnd(); + + glPopAttrib(); + + ScreenBlit(); + if(time>=maxTime) + exit=true; + } + gTexturesQualityModifier=0; +} + +void InterfaceFadeInInterfaceFromImage(tFileRef image,float maxTime) +{ + float startTime=TimeGetSeconds(); + float time=0; + int exit=false; + + glLoadIdentity(); + glTranslatef(0.0f,0.0f,-1.0f); + + while(!exit) + { + time=TimeGetSeconds()-startTime; + float alpha=1-(time/maxTime); + InterfaceDrawBackground(-1,image,kBackgroundXStretch,kBackgroundYStretch,0,0,alpha); + ScreenBlit(); + if(time>=maxTime) + exit=true; + } +} + +void InterfaceDrawStrings(char *stringBuffer,char *secondaryStringBuffer,tFileRef background) +{ + InterfaceDrawBackground(background,-1,0,0,0,0,1); + TextPrintfToBufferFormated(Vector(kTitlePosX,kTitlePosY),0.08,kTextAlignLeft,stringBuffer); + TextPrintfToBufferFormated(Vector(-0.9,0.5),0.05,kTextAlignLeft,secondaryStringBuffer); +// TextPrintfToBufferFormated(Vector(-1,0.97),0.03,kTextAlignLeft,gInterfaceMenuPath); + TextPrintBuffer(1,false); + TextClearBuffer(); + ScreenBlit(); +} + +void InterfaceDrawStatusBar(char *stringBuffer,char *secondaryStringBuffer,float status) +{ + InterfaceDrawBackground(-1,-1,0,0,0,0,1); + TextPrintfToBufferFormated(Vector(kTitlePosX,kTitlePosY),0.08,kTextAlignLeft,stringBuffer); + TextPrintfToBufferFormated(Vector(-0.9,0.5),0.05,kTextAlignLeft,secondaryStringBuffer); +// TextPrintfToBufferFormated(Vector(-1,0.97),0.03,kTextAlignLeft,gInterfaceMenuPath); + TextPrintBuffer(1,false); + TextClearBuffer(); + + glLoadIdentity(); + glTranslatef(0.0f,0.0f,-1.0f); + + glPushAttrib(GL_DEPTH_BUFFER_BIT+GL_LIGHTING_BIT+GL_COLOR_BUFFER_BIT+GL_CURRENT_BIT); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + + gTexturesQualityModifier=-255; + TexturesSelectTex(FileGetReference("progress.pct")); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(status,1); glVertex2f(-kBackgroundXStretch*0.8+kBackgroundXStretch*1.6*status,-kBackgroundYStretch*0.1); + glTexCoord2d(status,0); glVertex2f(-kBackgroundXStretch*0.8+kBackgroundXStretch*1.6*status,kBackgroundYStretch*0.1); + glTexCoord2d(0,1); glVertex2f(-kBackgroundXStretch*0.8,-kBackgroundYStretch*0.1); + glTexCoord2d(0,0); glVertex2f(-kBackgroundXStretch*0.8,kBackgroundYStretch*0.1); + glEnd(); + + glPopAttrib(); + + ScreenBlit(); + gTexturesQualityModifier=0; +} + +void MenuSubTitleCallback(char *text,int selection,void *menu) +{ + TextPrintfToBufferFormated(Vector(-0.85,0.6),0.05,kTextAlignLeft|kTextAutoWrap,text); +} + + +int InterfaceGetUserConfirmation(char *title,char *text) +{ + tInterfaceMenuDescribtion menu; + InterfaceInitMenu(&menu,2,title); + + menu.RenderCallback=(void(*)(void*,int,void*))MenuSubTitleCallback; + menu.userData=text; + menu.itemsYPos=0.2; + + char itemStrings[][32]={"OK","Cancel"}; + for(int i=0;i0) + stringBuffer[--len]='\0'; + if(ch>=(fullCharSet?' ':'-')&&ch<='z'&&len=1000000) + sprintf(str,"$%d,%03d,%03d",cash/1000000,(cash/1000)%1000,cash%1000); + else if(cash>=1000) + sprintf(str,"$%d,%03d",cash/1000,cash%1000); + else + sprintf(str,"$%d",cash); +} + +void MakeNumString(int i,char *str) +{ + if((i%10)==1&&i!=11) + sprintf(str,"1st"); + else if((i%10)==2&&i!=12) + sprintf(str,"2nd"); + else if((i%10)==3&&i!=13) + sprintf(str,"3rd"); + else + sprintf(str,"%dth",i); +} + +void InterfaceInitMenu(tInterfaceMenuDescribtion *menu,int numItems,char *title) +{ + menu->items=(tInterfaceMenuItemDescribtion*)MemoryAllocateBlock(sizeof(tInterfaceMenuItemDescribtion)*numItems); + + strcpy(menu->title,title); + menu->initialSelection=0; + menu->numItems=numItems; + menu->image=-1; + menu->background=FileGetReference(kInterfaceBackgroundTexture); + menu->scrollEnable=false; + menu->joinDisable=false; + menu->enterAlwaysOK=false; + menu->easterEggEnable=false; + menu->menuTypeable=false; + menu->easterEggScrollPos=-10; + menu->easterEggString=0; + menu->imageXScale=kBackgroundXStretch; + menu->imageYScale=kBackgroundYStretch; + menu->imageAlpha=1.0; + menu->imageXPos=0; + menu->imageYPos=0; + menu->itemsXPos=-0.9; + menu->itemsYPos=0.55; + menu->minScroll=-0.75; + menu->type[0]='\0'; + menu->returnOnSpace=false; + + menu->cursorShowPos=10000; + menu->cursorPos=0; + menu->descPos=Vector(0.3,0.55); + menu->mousePos=GetMousePos(); + menu->RenderCallback=NULL; + menu->TimerCallback=NULL; + menu->userData=NULL; + for(int i=0;iitems[i].sel=0; + menu->items[i].label[0]='\0'; + menu->items[i].describtion[0]='\0'; + menu->items[i].type[0]='\0'; + menu->items[i].size=0.05; + menu->items[i].lineSpacing=0.15; + menu->items[i].maxTypeXPos=0.95; + menu->items[i].maxTypeLength=255; + menu->items[i].sliderPos=0; + menu->items[i].sliderTransparency=0; + menu->items[i].r=1; + menu->items[i].g=1; + menu->items[i].b=1; + menu->items[i].flags=0; + } +} + +void InterfaceDisposeMenu(tInterfaceMenuDescribtion *menu) +{ + MemoryFreeBlock((tInterfaceMenuItemDescribtion*)menu->items); +} + +void InterfaceMenuRender(tInterfaceMenuDescribtion *menu,int selection,float backgroundAlpha) +{ + static float lt=0; + float t=TimeGetSeconds(); + float dt=t-lt; + lt=t; + + gTexturesQualityModifier=-255; + if(menu->background!=kFileErr) + InterfaceDrawBackground(menu->background,menu->image,menu->imageXScale,menu->imageYScale,menu->imageXPos,menu->imageYPos,menu->imageAlpha*backgroundAlpha); + + gTexturesQualityModifier=0; + if(menu->RenderCallback) + menu->RenderCallback(menu->userData,selection,menu); + gTexturesQualityModifier=-255; + + + float line=menu->itemsYPos; + float selOffset=line; + + if(selection!=-1&&menu->scrollEnable) + { + for(int i=0;iitems[i].flags&kInterfaceMenuItemFixedPos)) + selOffset-=menu->items[i].lineSpacing; + float lastOffset=selOffset; + for(int i=selection;inumItems;i++) + if(!(menu->items[i].flags&kInterfaceMenuItemFixedPos)) + lastOffset-=menu->items[i].lineSpacing; + + if(lastOffsetminScroll) + line+=((menu->itemsYPos-selOffset)/(menu->itemsYPos-lastOffset))*((menu->itemsYPos-lastOffset)-(menu->itemsYPos-menu->minScroll)); + } + + for(int i=0;inumItems;i++) + { + if((line<=menu->itemsYPos+menu->items[i].lineSpacing*0.5&&line>=menu->minScroll)||(!menu->scrollEnable)||menu->items[i].flags&kInterfaceMenuItemFixedPos) + { + char hiddenType[256]; + int ch; + for(ch=0;chitems[i].type);ch++) + hiddenType[ch]='*'; + hiddenType[ch]='\0'; + + char str[256]; + int j=0; + int img=false; + if(menu->items[i].flags&kInterfaceMenuItemTypeable) + do + { + char *type=(menu->items[i].flags&kInterfaceMenuItemTypeHidden?hiddenType:menu->items[i].type)+j; + sprintf(str,"%s\255#a\255%s",menu->items[i].label,type); + if(j) + if(type[-1]=='\255') + img=!img; + j++; + } + while((TextWidth(str,menu->items[i].size*(1+0.1*menu->items[i].sel))+menu->itemsXPos*kTextXPos>kTextXPos*menu->items[i].maxTypeXPos||img)&&j!=strlen(menu->items[i].type)); + else + strcpy(str,menu->items[i].label); + float fullLength=TextWidth(str,menu->items[i].size*(1+0.1*menu->items[i].sel)); + float visRatio=fullLength/(kTextXPos*(menu->items[i].maxTypeXPos-menu->itemsXPos)); + float textshift=0; + if(visRatio>1) + { + float scroll=menu->items[i].scroll/1.5f; + if(selection==i) + menu->items[i].scroll+=dt; + else if(menu->items[i].scroll>2) + menu->items[i].scroll=0; + else if(menu->items[i].scroll>1) + menu->items[i].scroll=2-menu->items[i].scroll; + else + menu->items[i].scroll-=dt; + + float scrollsize; + if(scroll<1) + scrollsize=fullLength*(1-(1-(1/(visRatio+0.1)))*scroll); + else if(scroll<2) + scrollsize=fullLength*(1-(1-(1/(visRatio+0.1)))*(2-scroll)); + else + scrollsize=fullLength; + char tmp[256]; + strcpy(tmp,str); + j=0; + img=false; + char color=-1; + do + { + strcpy(str,tmp+j); + if(j) + if(tmp[j-1]=='\255') + { + img=!img; + if(img&&tmp[j]=='#') + color=tmp[j+1]; + } + j++; + } + while((TextWidth(str,menu->items[i].size*(1+0.1*menu->items[i].sel))>scrollsize||img)&&j!=strlen(tmp)); + textshift=scrollsize-TextWidth(str,menu->items[i].size*(1+0.1*menu->items[i].sel)); + if(color!=-1) + { + strcpy(tmp,str); + sprintf(str,"\255#%c\255%s",color,tmp); + } +/* int scrollpos=0; + + if(scroll<1) + scrollpos=(1-(1/visRatio))*strlen(str)*scroll; + else if(scroll<2) + scrollpos=(1-(1/visRatio))*strlen(str)*(2-scroll); + memmove(str,str+scrollpos,strlen(str+scrollpos)+1);*/ + } + if(menu->items[i].scroll<0) + menu->items[i].scroll=0; + img=false; + int l=strlen(str); + while((TextWidth(str,menu->items[i].size*(1+0.1*menu->items[i].sel))+menu->itemsXPos*kTextXPos>kTextXPos*menu->items[i].maxTypeXPos||img)&&*str) + { + if(str[l-1]=='\255') + img=!img; + str[l-1]='\0'; + l--; + } + + + + if(menu->items[i].flags&kInterfaceMenuItemTypeable&&selection==i&&(int)(TimeGetSeconds()*4)%2) + sprintf(str,"%s%s",str,"_"); + + float a=(menu->items[i].flags&kInterfaceMenuItemDisabled?0.5:1)*(0.8+(0.2*menu->items[i].sel*0.5*(sin(TimeGetSeconds()*8)+1) )); + if(menu->items[i].flags&kInterfaceMenuItemFixedPos) + TextPrintfToBufferFormatedColored(Vector(menu->items[i].fixedX,menu->items[i].fixedY+menu->items[i].size*0.1*menu->items[i].sel) + ,menu->items[i].size*(1+0.1*menu->items[i].sel) + ,kTextAlignLeft + ,menu->items[i].r + ,menu->items[i].g + ,menu->items[i].b + ,a + ,str + ); + else + TextPrintfToBufferFormatedColored(Vector(menu->itemsXPos+textshift,line+menu->items[i].size*0.1*menu->items[i].sel) + ,menu->items[i].size*(1+0.1*menu->items[i].sel) + ,kTextAlignLeft + ,menu->items[i].r + ,menu->items[i].g + ,menu->items[i].b + ,a + ,str + ); + if(selection==i) + if(menu->items[i].flags&kInterfaceMenuItemFixedPos) + menu->cursorPos=(menu->items[i].fixedY-menu->items[i].size*1.3)*kScreenYPos; + else + menu->cursorPos=(line-menu->items[i].size*1.3)*kScreenYPos; + + if(menu->items[i].flags&kInterfaceMenuItemSlider) + { + glLoadIdentity(); + glTranslatef(0.0f,0.0f,-1.0f); + + glPushAttrib(GL_DEPTH_BUFFER_BIT+GL_LIGHTING_BIT+GL_COLOR_BUFFER_BIT+GL_CURRENT_BIT); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + + float y=(line-0.05)*kScreenYPos; + + glColor4f(1,1,1,(1-menu->items[i].sliderTransparency)*backgroundAlpha); + TexturesSelectTex(FileGetReference(kInterfaceMenuSlideBarTexture)); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex2f((menu->itemsXPos-0.05)*kScreenXPos+kInterfaceMenuSlideBarStart+kInterfaceMenuSlideBarWidth, y-kInterfaceMenuSlideBarHeight); + glTexCoord2d(1,0); glVertex2f((menu->itemsXPos-0.05)*kScreenXPos+kInterfaceMenuSlideBarStart+kInterfaceMenuSlideBarWidth, y+kInterfaceMenuSlideBarHeight); + glTexCoord2d(0,1); glVertex2f((menu->itemsXPos-0.05)*kScreenXPos+kInterfaceMenuSlideBarStart, y-kInterfaceMenuSlideBarHeight); + glTexCoord2d(0,0); glVertex2f((menu->itemsXPos-0.05)*kScreenXPos+kInterfaceMenuSlideBarStart, y+kInterfaceMenuSlideBarHeight); + glEnd(); + + TexturesSelectTex(FileGetReference(kInterfaceMenuSliderTexture)); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex2f((menu->itemsXPos-0.05)*kScreenXPos+menu->items[i].sliderPos*kInterfaceMenuSlideBarWidth+kInterfaceMenuSlideBarStart+kInterfaceMenuSliderSize, y-kInterfaceMenuSliderSize); + glTexCoord2d(1,0); glVertex2f((menu->itemsXPos-0.05)*kScreenXPos+menu->items[i].sliderPos*kInterfaceMenuSlideBarWidth+kInterfaceMenuSlideBarStart+kInterfaceMenuSliderSize, y+kInterfaceMenuSliderSize); + glTexCoord2d(0,1); glVertex2f((menu->itemsXPos-0.05)*kScreenXPos+menu->items[i].sliderPos*kInterfaceMenuSlideBarWidth+kInterfaceMenuSlideBarStart-kInterfaceMenuSliderSize, y-kInterfaceMenuSliderSize); + glTexCoord2d(0,0); glVertex2f((menu->itemsXPos-0.05)*kScreenXPos+menu->items[i].sliderPos*kInterfaceMenuSlideBarWidth+kInterfaceMenuSlideBarStart-kInterfaceMenuSliderSize, y+kInterfaceMenuSliderSize); + glEnd(); + + glPopAttrib(); + } + } + if(!(menu->items[i].flags&kInterfaceMenuItemFixedPos)) + line-=menu->items[i].lineSpacing; + } + TextPrintfToBufferFormated(Vector(kTitlePosX,kTitlePosY),0.08,kTextAlignLeft,menu->title); + + float a=2-(TimeGetSeconds()-giTunesLastStatusUpdate)*0.4; + if(a>0) + { + char str[1024]; + if(giTunesPlaying==kITunesPlaying) + sprintf(str,"%s - %s",giTunesArtist,giTunesSong); + else if(giTunesPlaying==kITunesPaused) + strcpy(str,"Paused"); + else + strcpy(str,"Stopped"); + float screenRatio=(float)gConfig->screenXSize/(float)gConfig->screenYSize; + float maxWidth=(0.1+screenRatio/(4.0/3.0))*kTextXPos; + float scale=1; + while(TextWidth(str,0.04*scale)>maxWidth) + scale*=0.9; + //printf("%f,%f/%f\n",size,TextWidth(str,size),maxWidth); + TextPrintfToBufferFormatedColored(Vector(-0.1,0.95+0.05*scale),0.04*scale,kTextAlignLeft,1,1,1,a,str); + } + + glLoadIdentity(); + glTranslatef(0.0f,0.0f,-1.0f); + + glPushAttrib(GL_DEPTH_BUFFER_BIT+GL_LIGHTING_BIT+GL_COLOR_BUFFER_BIT+GL_CURRENT_BIT); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + glColor4f(1,1,1,backgroundAlpha*(menu->menuTypeable&&*menu->type)?0.5:1); + TexturesSelectTex(FileGetReference(kInterfaceMenuPointerTexture)); + + glBegin(GL_TRIANGLE_STRIP); + float y=menu->cursorShowPos; + glTexCoord2d(1,1); glVertex2f((menu->itemsXPos-0.05)*kScreenXPos+kInterfaceMenuPointerSize,y-kInterfaceMenuPointerSize); + glTexCoord2d(1,0); glVertex2f((menu->itemsXPos-0.05)*kScreenXPos+kInterfaceMenuPointerSize,y+kInterfaceMenuPointerSize); + glTexCoord2d(0,1); glVertex2f((menu->itemsXPos-0.05)*kScreenXPos-kInterfaceMenuPointerSize,y-kInterfaceMenuPointerSize); + glTexCoord2d(0,0); glVertex2f((menu->itemsXPos-0.05)*kScreenXPos-kInterfaceMenuPointerSize,y+kInterfaceMenuPointerSize); + glEnd(); + + + glPopAttrib(); + + if(selection!=-1) + TextPrintfToBufferFormatedColored(menu->descPos,0.03,kTextAlignLeft|kTextAutoWrap + ,1,1,1,menu->items[selection].flags&kInterfaceMenuItemDisabled?0.45:1 + ,menu->items[selection].describtion); + + + + TextPrintfToBufferFormatedColoredTilted(Vector(0+sin(menu->easterEggScrollPos*5)*0.1,-menu->easterEggScrollPos),0.05,kTextAlignMiddle,-cos(menu->easterEggScrollPos*5)*0.3,1,0.5,0,1,gEasterEggStrings[menu->easterEggString]); + + TextPrintBuffer(backgroundAlpha,false); + TextClearBuffer(); + ScreenBlit(); + gTexturesQualityModifier=0; +} + +#define kMenuAnimationTime 0.2 +#define kMenuAnimationMaxXZoom 2.5 +#define kMenuAnimationMaxYZoom 0.5 + +void InterfaceTextBufferZoomAnimation(tFileRef background,int in) +{ + float startTime=TimeGetSeconds(); + float time=0; + while(timeitemsYPos; + float minDist=1000; + for(int i=0;inumItems;i++) + { + float dist=fabs(y-menu->items[i].size-mousePos.y); + if(mousePos.xitemsXPos) + dist+=fabs(mousePos.x-menu->itemsXPos); + if(menu->items[i].flags&kInterfaceMenuItemSlider) + { + if(mousePos.x>menu->itemsXPos+(kInterfaceMenuSlideBarStart+kInterfaceMenuSlideBarWidth)/kScreenXPos) + dist+=fabs(mousePos.x-(menu->itemsXPos+(kInterfaceMenuSlideBarStart+kInterfaceMenuSlideBarWidth)/kScreenXPos)); + } + else + if(mousePos.x>menu->itemsXPos+TextWidth(menu->items[i].label,menu->items[i].size)/kScreenXPos) + dist+=fabs(mousePos.x-(menu->itemsXPos+TextWidth(menu->items[i].label,menu->items[i].size)/kScreenXPos)); + if(distmousePos)) + *selection=i; + mouseSel=true; + } + } + + y-=menu->items[i].lineSpacing; + + } + if(mousePos.x>menu->itemsXPos+kInterfaceMenuSlideBarStart/kScreenXPos&&mousePos.xitemsXPos+(kInterfaceMenuSlideBarStart+kInterfaceMenuSlideBarWidth)/kScreenXPos) + mouseBar=(mousePos.x-(menu->itemsXPos+kInterfaceMenuSlideBarStart/kScreenXPos))/(kInterfaceMenuSlideBarWidth/kScreenXPos)+0.08; + menu->mousePos=mousePos; +*/ + int key; + char ch=GetKeyInput(&key); + gLastMenuChar=ch; + if(ch||key) + gStartIdleTime=TimeGetSeconds(); + +/* if(key==kInterfaceMouseDown&&mouseSel) + if(menu->items[*selection].flags&kInterfaceMenuItemSlider) + { + if(mouseBar>menu->items[*selection].sliderPos) + key=kInterfaceKeyRight; + else + key=kInterfaceKeyLeft; + } + else if(menu->items[*selection].flags&kInterfaceMenuItemArrowInput) + key=kInterfaceKeyRight; + else if(!(menu->items[*selection].flags&kInterfaceMenuItemTypeable)) + key=kInterfaceKeyEnter; +*/ + switch(key) + { + //cursor up/down movement + case kInterfaceKeyDown: + PlayInterfaceSound(FileGetReference("menu.wav")); + (*selection)++; + if(*selection>=menu->numItems) + *selection=0; + break; + + case kInterfaceKeyUp: + PlayInterfaceSound(FileGetReference("menu.wav")); + (*selection)--; + if(*selection<0) + *selection=menu->numItems-1; + break; + + //cursor left/right movement (if enabled) + case kInterfaceKeyLeft: + if(menu->items[*selection].flags&kInterfaceMenuItemArrowInput&&!(menu->items[*selection].flags&kInterfaceMenuItemDisabled)) + { + PlayInterfaceSound(FileGetReference("toggle.wav")); + *selection|=kInterfaceMenuLeftArrow; + if(GetInterfaceKey(kInterfaceKeyEasterEgg)) + *selection|=kInterfaceMenuEasterEgg; + return true; + } + break; + + case kInterfaceKeyRight: + if(menu->items[*selection].flags&kInterfaceMenuItemArrowInput&&!(menu->items[*selection].flags&kInterfaceMenuItemDisabled)) + { + PlayInterfaceSound(FileGetReference("toggle.wav")); + *selection|=kInterfaceMenuRightArrow; + if(GetInterfaceKey(kInterfaceKeyEasterEgg)) + *selection|=kInterfaceMenuEasterEgg; + return true; + } + break; + + //esc key + case kInterfaceKeyDelete: + if((menu->items[*selection].flags&kInterfaceMenuItemTypeable)||(menu->menuTypeable)) + break; + case kInterfaceKeyEsc: + PlayInterfaceSound(FileGetReference("esc.wav")); + if(menu->menuTypeable&&*menu->type) + { + *menu->type='\0'; + break; + } + InterfaceMenuZoomAnimation(menu,*selection,false); + *selection=kInterfaceMenuEsc; + return true; + + case kInterfaceKeyEasterEgg: + if(menu->easterEggEnable) + menu->easterEggScrollPos=1.0; + menu->easterEggString=RandomInt(0,kNumEasterEggStrings); + break; + + case kInterfaceKeyR: + if(menu->easterEggEnable) + { + InterfaceMenuZoomAnimation(menu,*selection,false); + *selection=kInterfaceMenuReplayKey; + return true; + } + break; + + //enter key + case kInterfaceKeyReturn: + case kInterfaceKeyEnter: + if(menu->menuTypeable&&*menu->type) + return true; + PlayInterfaceSound(FileGetReference("select.wav")); + if(menu->enterAlwaysOK) + { + InterfaceMenuZoomAnimation(menu,*selection,false); + *selection=kInterfaceMenuOK; + return true; + } + if(!(menu->items[*selection].flags&(kInterfaceMenuItemTypeable|kInterfaceMenuItemDisabled|kInterfaceMenuItemArrowInput))) + InterfaceMenuZoomAnimation(menu,*selection,false); + if(!(menu->items[*selection].flags&kInterfaceMenuItemDisabled)) + return true; + + case kInterfaceKeySpace: + if(menu->returnOnSpace) + { + InterfaceMenuZoomAnimation(menu,*selection,false); + *selection|=kInterfaceMenuSpaceFlag; + return true; + } + break; + } + + if(menu->items[*selection].flags&kInterfaceMenuItemTypeable||menu->menuTypeable&&!(menu->menuTypeable==kMenuTypeableReggie&&!gReggieConnected)) + { + gInterfaceType=true; + if(ch) + { + int len=strlen((menu->items[*selection].flags&kInterfaceMenuItemTypeable)?menu->items[*selection].type:menu->type); + if((ch==0x7f||ch==0x08)&&len>0) + if(menu->items[*selection].flags&kInterfaceMenuItemTypeable) + menu->items[*selection].type[--len]='\0'; + else + menu->type[--len]='\0'; + if(ch>=' '&&ch<='z'&&ch!='%'&&lenitems[*selection].maxTypeLength) + { + PlayInterfaceSound(FileGetReference("type.wav")); + if(menu->items[*selection].flags&kInterfaceMenuItemTypeable) + { + menu->items[*selection].type[len++]=ch; + menu->items[*selection].type[len]='\0'; + } + else + { + menu->type[len++]=ch; + menu->type[len]='\0'; + } + } + } + } + else + gInterfaceType=menu->items[*selection].flags&kInterfaceMenuItemTypeable; + return false; +} + +#define kMenuSelFadeTime 0.2 + +void InterfaceFadeMenuSelection(tInterfaceMenuDescribtion *menu,float dt,int selection) +{ + for(int i=0;inumItems;i++) + { + menu->items[i].sel-=dt/kMenuSelFadeTime; + if(menu->items[i].sel<0) + menu->items[i].sel=0; + } + if( menu->items[selection].sel<1) + menu->items[selection].sel=1; + if(menu->cursorShowPos>10) + menu->cursorShowPos=menu->cursorPos; + + if(menu->cursorShowPos>menu->cursorPos) + { + menu->cursorShowPos-=dt+dt*10*fabs(menu->cursorShowPos-menu->cursorPos); + if(menu->cursorShowPoscursorPos) + menu->cursorShowPos=menu->cursorPos; + } + else + { + menu->cursorShowPos+=dt+dt*10*fabs(menu->cursorShowPos-menu->cursorPos); + if(menu->cursorShowPos>menu->cursorPos) + menu->cursorShowPos=menu->cursorPos; + } +} + +int InterfaceGetUserMenuSelection(tInterfaceMenuDescribtion *menu) +{ + float lastTime=TimeGetSeconds(); + int selection=menu->initialSelection; + int count=0; + do{ + if(selection>=menu->numItems||selection<0) + selection=0; + + if(gJoinFlag) + if(!menu->joinDisable) + { + gJoinFlag=false; + DirectJoinGame(gJoinHost); + } + else + return kInterfaceMenuEsc; + + if(!gSystemSuspended||(count++%100==0)) + InterfaceMenuRender(menu,selection,1); + SystemPoll(false); + SystemYieldTime(kMenuFrameTime+lastTime); + + float time=TimeGetSeconds(); + + InterfaceFadeMenuSelection(menu,time-lastTime,selection); + menu->easterEggScrollPos-=kEasterEggScrollSpeed*(time-lastTime); + lastTime=time; + if(menu->TimerCallback) + { + int val=menu->TimerCallback(menu->userData); + if(val!=-1) + return val; + } + }while(!InterfaceMenuInput(menu,&selection)); + return selection; +} + +#define kScrollTextSize 0.05 + +int TextPrintFile(tFileRef fileId,float yStart) +{ + float y=yStart; + char *fileData=(char*)FileGetDataPtr(fileId); + char *filePos=fileData; + int fileLenght=FileGetSize(fileId); + int linedrawn=false; + while(filePos1)a=1; + + if(y<=1+3*kScrollTextSize&&y>=-1-3*kScrollTextSize) + { + if(line[0]=='\255'&&line[1]!='#') + TextPrintfToBufferFormatedColored(Vector(0,y),kScrollTextSize*5,kTextAlignMiddle,1,1,1,a,line); + else + TextPrintfToBufferFormatedColored(Vector(0,y),kScrollTextSize,kTextAlignMiddle,1,1,1,a,line); + linedrawn=true; + } + y-=kScrollTextSize*3; + } +// InterfaceDrawBackground(-1,-1,0,0,0,0,1); + InterfaceRenderReplay(NULL,0,NULL); + TextPrintBuffer(1,false); + TextClearBuffer(); + + ScreenBlit(); + return linedrawn; +} + +void TextScrollFile(tFileRef fileId) +{ + while(GetInterfaceKey(kInterfaceKeyReturn)||GetInterfaceKey(kInterfaceKeyEnter)||GetInterfaceKey(kInterfaceKeyEsc)); + float startTime=TimeGetSeconds(); + int start=false; + int done=false; + while(!(GetInterfaceKey(kInterfaceKeyReturn)||GetInterfaceKey(kInterfaceKeyEnter)||GetInterfaceKey(kInterfaceKeyEsc)||GetInterfaceKey(kInterfaceKeyDelete)||done)) + { + SystemPoll(false); + float time=TimeGetSeconds()-startTime; + float y=-1.2+time*0.2; + if(!TextPrintFile(fileId,y)){ + if(start) + done=true; + } + else + start=true; + SystemPoll(false); + } + FlushKeys(); +} + +void TeaserScreen() +{ + if(RT3_IsRegistered()) + return; + + tInterfaceMenuDescribtion menu; + InterfaceInitMenu(&menu,2,"Get the full version now!"); + +// menu.userData=text; + menu.image=FileGetReference("teaser.png"); + menu.imageXScale=0.2; + menu.imageYScale=0.2; + menu.imageXPos+=0.2; + menu.imageYPos+=0.1; + + char itemStrings[][32]={"Later...","Buy It Now!"}; + for(int i=0;ifullscreen) + { + gConfig->fullscreen=false; + ScreenReInit(); + gConfig->fullscreen=true; + } + ASWReg_Initialization(); + ASWReg_ShowDialog(true); + if(gConfig->fullscreen) + ScreenReInit(); + +// RT3_LaunchRegisterApp(); +// Exit(); + } + InterfaceDisposeMenu(&menu); +} \ No newline at end of file diff --git a/source/interfaceutil.h b/source/interfaceutil.h new file mode 100644 index 0000000..e311b88 --- /dev/null +++ b/source/interfaceutil.h @@ -0,0 +1,130 @@ +#ifndef __INTERFACEUTIL +#define __INTERFACEUTIL + +#include "vectors.h" +#include "fileio.h" + +enum{ + kInterfaceMenuItemDisabled=1<<0, + kInterfaceMenuItemArrowInput=1<<1, + kInterfaceMenuItemTypeable=1<<2, + kInterfaceMenuItemTypeHidden=1<<3, + kInterfaceMenuItemSlider=1<<4, + kInterfaceMenuItemFixedPos=1<<5 +}; + +enum{ + kInterfaceMenuEsc=1<<31, + kInterfaceMenuLeftArrow=1<<30, + kInterfaceMenuRightArrow=1<<29, + kInterfaceMenuOK=1<<28, + kInterfaceMenuEasterEgg=1<<27, + kInterfaceMenuReplayKey=1<<26, + kInterfaceMenuSpaceFlag=1<<25, +}; + +#define kInterfaceBackgroundTexture "background.tif" + +#define kInterfaceMenuSlideBarTexture "bar.tif" +#define kInterfaceMenuSlideBarHeight 0.01 +#define kInterfaceMenuSlideBarStart 0.35 +#define kInterfaceMenuSlideBarWidth 0.45 + +#define kInterfaceMenuSliderTexture "slider.tif" +#define kInterfaceMenuSliderSize 0.02 +#define kInterfaceKeyImageSize 0.06 + +#define kBackgroundXStretch 0.622 +#define kBackgroundYStretch 0.43 + +#define kInterfaceStatusTexture kInterfaceMenuSlideBarTexture + +#define kInterfaceMenuItemMask (~(kInterfaceMenuLeftArrow|kInterfaceMenuRightArrow|kInterfaceMenuEasterEgg|kInterfaceMenuReplayKey|kInterfaceMenuSpaceFlag)) + +typedef struct{ + char label[256]; + char describtion[512]; + char type[256]; + int flags; + int maxTypeLength; + float maxTypeXPos; + float sliderPos; + float sliderTransparency; + float sel; + float size; + float scroll; + float lineSpacing; + float r,g,b; + float fixedX,fixedY; +} tInterfaceMenuItemDescribtion; + +enum{ + kMenuTypeableOff, + kMenuTypeableOn, + kMenuTypeableReggie +}; + +typedef struct{ + char title[256]; + char type[256]; + int initialSelection; + int numItems; + int scrollEnable; + int menuTypeable; + tFileRef image,background; + int enterAlwaysOK; + int returnOnSpace; + int easterEggEnable; + int easterEggString; + int joinDisable; + float cursorPos,cursorShowPos; + float easterEggScrollPos; + float imageXScale,imageYScale; + float imageXPos,imageYPos; + float imageAlpha; + float itemsXPos,itemsYPos; + float minScroll; + tVector2 mousePos; + tVector2 descPos; + tInterfaceMenuItemDescribtion *items; + void *userData; + void (*RenderCallback)(void*,int,void*); + int (*TimerCallback)(void*); +} tInterfaceMenuDescribtion; + +#define kTitlePosX -0.93 +#define kTitlePosY 0.98 + +#define kInterfaceMenuPointerTexture "menupointer.tif" +#define kInterfaceMenuPointerSize 0.02 + +extern int gJoinFlag; +extern char gJoinHost[]; +extern char gLastMenuChar; +extern int gInterfaceType; +extern float gStartIdleTime; + +int InterfaceGetUserInputString(char *prompt,char *stringBuffer,int bufferSize,int fullCharSet,int hideType); +int InterfaceGetUserMenuSelection(tInterfaceMenuDescribtion *menu); +void InterfaceDrawStrings(char *stringBuffer,char *secondaryStringBuffer,tFileRef background); +void InterfaceDisplayMessage(tFileRef background,char *stringBuffer,char *secondaryStringBuffer); +void CashString(int cash, char *str); +void MakeNumString(int i,char *str); +void TextScrollFile(tFileRef text); +void InterfaceTextBufferZoomAnimation(tFileRef background,int in); +void InterfaceMenuZoomAnimation(tInterfaceMenuDescribtion *menu,int selection,int in); +void InterfaceInitMenu(tInterfaceMenuDescribtion *menu,int numItems,char *title); +void InterfaceDisposeMenu(tInterfaceMenuDescribtion *menu); +void InterfaceDrawBackground(tFileRef background,tFileRef image,float imageXScale,float imageYScale,float imageXPos,float imageYPos,float alpha); +int InterfaceGetUserConfirmation(char *title,char *text); +void InterfaceFadeInImageFromBlack(float xSize,float ySize,tFileRef image,float maxTime); +void InterfaceFadeInInterfaceFromImage(tFileRef image,float maxTime); +void InterfaceFadeInImageFromImage(float xSize,float ySize,tFileRef image,float xSize2,float ySize2,tFileRef image2,float maxTime); +void InterfaceDrawBackgroundFade(float opacity,int showtop); +void InterfaceDrawStatusBar(char *stringBuffer,char *secondaryStringBuffer,float status); +void TeaserScreen(); + + +void InterfaceRenderReplay(void *userData,int selection,void *menu); + +#endif \ No newline at end of file diff --git a/source/lights.cpp b/source/lights.cpp new file mode 100644 index 0000000..c0cb970 --- /dev/null +++ b/source/lights.cpp @@ -0,0 +1,235 @@ +#include +#include +#include "vectors.h" +#include "environment.h" +#include "entities.h" +#include "textures.h" +#include "renderframe.h" +#include "roads.h" +#include "collision.h" +#include "config.h" +#include "stencil.h" + +#define kLightOffset 0.6 +#define kLightReflectionOffset 15 + +void RenderEntityLights(tGameEntity *entity,int numLights,int lightFlags,float reflectionDim,tLightDefinition *lights) +{ + glPushAttrib(GL_DEPTH_BUFFER_BIT+GL_CURRENT_BIT+GL_LIGHTING_BIT+GL_COLOR_BUFFER_BIT+GL_FOG_BIT); + glDepthMask(false); + glDisable(GL_LIGHTING); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE); + glDisable(GL_FOG); + TexturesSelectTex(FileGetReference("Particle.pct")); + + for(int i=0;idir**MatrixGetZVector(gCameraEntity->dir); + //is the light pointing towards the camera? + + if(lights[i].type==kLightTypeDirectionlessDot||lights[i].type==kLightTypeDirectionlessDotReflective) + visibility=1; + else if(lights[i].type==kLightTypeSpecularDot) + visibility=sign(visibility)*powf(visibility*!((lights[i].pos*entity->dir+entity->pos)-gCameraEntity->pos)**MatrixGetZVector(gCameraEntity->dir),100); + + if(visibility>0){ + if(visibility>1)visibility=1; + + float size=lights[i].size; + + //draw particle texture + + tVector3 lightPos=lights[i].pos*entity->dir+entity->pos; + float camDist=~(lightPos-gCameraEntity->pos); + tVector3 drawlightPos=lightPos-(lightPos-gCameraEntity->pos)*kLightOffset/camDist; + + size*=(camDist-kLightOffset)/camDist; + + SetupTranslation(drawlightPos,gCameraEntity->dir); + glColor4f(lights[i].rgb.x,lights[i].rgb.y,lights[i].rgb.z,visibility); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex2f(size,size); + glTexCoord2d(1,0); glVertex2f(size,-size); + glTexCoord2d(0,1); glVertex2f(-size,size); + glTexCoord2d(0,0); glVertex2f(-size,-size); + glEnd(); + + //does the light reflect on the ground? + if(lights[i].type==kLightTypeDotRefelective||lights[i].type==kLightTypeDirectionlessDotReflective) + { + int lastRoadIndex=entity->lastRoadIndex; + int surface=-1; + lightPos=lightPos-Vector(0,GetGroundOffset(lightPos,&lastRoadIndex,NULL,&surface),0); + float camDist=~(lightPos-gCameraEntity->pos); + float size=lights[i].size; + if(camDist>2*kLightReflectionOffset) + { + drawlightPos=lightPos-(lightPos-gCameraEntity->pos)*kLightReflectionOffset/camDist; + size*=(camDist-kLightReflectionOffset)/camDist; + } + else + drawlightPos=lightPos; + //is the ground reflective + if(surface!=-1) + if(gSurfaceTypes->types[surface].reflectionEnable) + { + //draw reflection texture + SetupTranslation(drawlightPos,gCameraEntity->dir); + if(camDist<=2*kLightReflectionOffset) + glDisable(GL_DEPTH_TEST); + + visibility*=1-reflectionDim*0.6; + glColor4f(lights[i].rgb.x,lights[i].rgb.y,lights[i].rgb.z,visibility); + + TexturesSelectTex(FileGetReference("lightreflection.pct")); + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex2f(size,size); + glTexCoord2d(1,0); glVertex2f(size,-5*size); + glTexCoord2d(0,1); glVertex2f(-size,size); + glTexCoord2d(0,0); glVertex2f(-size,-5*size); + glEnd(); + TexturesSelectTex(FileGetReference("Particle.pct")); + #ifdef __POLYCOUNT + gPolyCount+=2; + #endif + glEnable(GL_DEPTH_TEST); + } + } + #ifdef __POLYCOUNT + gPolyCount+=2; + #endif + } + } + glPopAttrib(); +} + +#define kConeSections 16 +#define kConeLength 6.5 + +void DrawLightCone(float scale) +{ + tVector3 c[kConeSections]; + for(int i=0;i0) + { + float cClipPos=(1-(cClipDist/(cClipDist-baseClipDist))); + if(cClipPos<=0)return; + c[i]=c[i]*cClipPos; + } + for(int i=0;istencil&&gEnvironment->spotLightEnable) + { + //translate to draw the light cone + tVector3 lightPos=lights[i].pos*entity->dir+entity->pos; + tMatrix3 m; + MatrixIdentity(m); + *MatrixGetZVector(m)=lights[i].dir; + *MatrixGetXVector(m)=!(lights[i].dir%Vector(0,1,0)); + *MatrixGetYVector(m)=(lights[i].dir%*MatrixGetXVector(m)); + MatrixMult(m,entity->dir,m); + SetupTranslation(lightPos,m); + + //setup stencil buffer + //drawing light cones works just like stencil buffer shadow volumes + glDepthMask(GL_FALSE); + glEnable(GL_STENCIL_TEST); + glColorMask(0, 0, 0, 0); + glStencilFunc(GL_ALWAYS, 1, 0xffffffff); + + tVector3 camDir=*MatrixGetZVector(gCameraEntity->dir); + float clip1=(lightPos-gCameraEntity->pos)*camDir; + float clip2=((lightPos+(lights[i].dir*kConeLength*lights[i].size)*entity->dir)-gCameraEntity->pos)*camDir; + //test if the light cone volume intersects the clipping volume + + if(clip1>ClipDistance()&&clip2>ClipDistance()) + {//clip near and far (not implemented) + } + else if(clip1>ClipDistance()) + {//clip near (not implemented) + } + else if(clip2>ClipDistance()-20) + {//clip far + tMatrix3 mInv; + MatrixTranspose(m,mInv); + tVector3 clipPlaneDir=camDir*mInv; + glFrontFace(GL_CW); + glStencilOp(GL_KEEP, GL_INCR, GL_KEEP); + DrawLightConeClippedFar(lights[i].size*gStencilZoom,(ClipDistance()-20-clip1)/(clip2-clip1),clipPlaneDir); + + glFrontFace(GL_CCW); + glStencilOp(GL_KEEP, GL_DECR, GL_KEEP); + DrawLightConeClippedFar(lights[i].size*gStencilZoom,(ClipDistance()-20-clip1)/(clip2-clip1),clipPlaneDir); + } + else + {//no clip + glFrontFace(GL_CW); + glStencilOp(GL_KEEP, GL_INCR, GL_KEEP); + DrawLightCone(lights[i].size*gStencilZoom); + + glFrontFace(GL_CCW); + glStencilOp(GL_KEEP, GL_DECR, GL_KEEP); + DrawLightCone(lights[i].size*gStencilZoom); + } + } + glPopAttrib(); +} diff --git a/source/lights.h b/source/lights.h new file mode 100644 index 0000000..b2612b8 --- /dev/null +++ b/source/lights.h @@ -0,0 +1,9 @@ +#ifndef __LIGHTS +#define __LIGHTS + +#include "entities.h" + +void RenderEntityLights(tGameEntity *entity,int numLights,int lightFlags,float reflectionDim,tLightDefinition *lights); +void RenderEntityLightCones(tGameEntity *entity,int numLights,int lightFlags,tLightDefinition *lights); + +#endif \ No newline at end of file diff --git a/source/localtracker.h b/source/localtracker.h new file mode 100644 index 0000000..b5899db --- /dev/null +++ b/source/localtracker.h @@ -0,0 +1,15 @@ +#ifndef __LOCALTRACKER +#define __LOCALTRACKER + +#include "tracker.h" +extern int gNumLocalGames; +extern int gLocalGameListUpdate; +extern tGameListEntry gLocalGames[]; + +void InitLocalTracker(); +void LocalTrackerStartAdvertising(char *gameName); +void LocalTrackerStopAdvertising(); +void LocalTrackerSearchGamesInit(); +void LocalTrackerSearchGamesDone(); + +#endif \ No newline at end of file diff --git a/source/log.cpp b/source/log.cpp new file mode 100644 index 0000000..29f5d47 --- /dev/null +++ b/source/log.cpp @@ -0,0 +1,291 @@ +//log.cpp +//log [network] physics packets in order to replay the games + +#include +#include +#include "networkphysics.h" +#include "gamemem.h" +#include "log.h" +#include "gameinitexit.h" +#include "gameframe.h" +#include "network.h" + +typedef struct{ + int offset; + short size; + short type; +} tLogEntry; + +#define kLogSize (20*1024*1024) +#define kGhostLogSize (256*1024) +#define kIndexSize (256*1024) + +char* gPacketLog; +char *gGhostLog; +tLogEntry *gPacketLogIndex; +tLogEntry *gGhostIndex; +int gLogPos=0; +int gIndexPos=0; +int gGhostLogPos=0; +int gGhostIndexPos=0; + +void LogInit() +{ + gPacketLog=(char*)malloc(kLogSize); + gGhostLog=(char*)malloc(kGhostLogSize); + gPacketLogIndex=(tLogEntry*)malloc(kIndexSize*sizeof(tLogEntry)); + gGhostIndex=(tLogEntry*)malloc(kIndexSize*sizeof(tLogEntry)); +} + +void GetLogs(tFileRef *logs,int *logCount) +{ + *logCount=0; + for(int i=0;ikGhostLogSize!!!! + MemoryMove(gGhostLog,gPacketLog,gLogPos>kGhostLogSize?kGhostLogSize:gLogPos); + MemoryMove(gGhostIndex,gPacketLogIndex,gIndexPos*sizeof(tLogEntry)); + gGhostLogPos=gLogPos; + gGhostIndexPos=gIndexPos; +} + +int LogCompare(const void *a,const void *b) +{ + int aFrame=((tPhysicsMessage*)(gPacketLog+*(int*)a))->frame; + int bFrame=((tPhysicsMessage*)(gPacketLog+*(int*)b))->frame; + S32Swap(aFrame); + S32Swap(bFrame); + return aFrame-bFrame; +} + +void LogSort() +{ + qsort(gPacketLogIndex,gIndexPos,sizeof(tLogEntry),LogCompare); +} + +void LogReset() +{ + gLogPos=0; + gIndexPos=0; +} + +void GhostLogReset() +{ + gGhostLogPos=0; + gGhostIndexPos=0; +} + + +int LogGetPacket(int index,int *type,int *size,int log,void *buffer) +{ + if(log==kLogReplayLog||gGhostLogPos==0){ + if(indexnumLaps==-1) + { + int w=gGhostIndexPos; + S32Swap(w); + fwrite(&w,sizeof(int),1,f); + + w=gGhostLogPos; + S32Swap(w); + fwrite(&w,sizeof(int),1,f); + + w=gReplayOldFrameCount; + S32Swap(w); + fwrite(&w,sizeof(int),1,f); + + + for(int i=0;iframe-=(gBestLapTime!=0?gBestLapStart+5:gCurrentLapStart+5); + } + } + } + fwrite(gGhostLog,sizeof(char),gGhostLogPos,f); + for(int i=0;iframe+=(gBestLapTime!=0?gBestLapStart+5:gCurrentLapStart+5); + } + } + } + } + else + { + int w=gIndexPos; + S32Swap(w); + fwrite(&w,sizeof(int),1,f); + + w=gLogPos; + S32Swap(w); + fwrite(&w,sizeof(int),1,f); + + w=gReplayOldFrameCount; + S32Swap(w); + fwrite(&w,sizeof(int),1,f); + + for(int i=0;inumPlayers;i++) + if(gInfo->playerCars[i]==-1) + return false; + if(gInfo->map==-1) + return false; + if(gInfo->environment==-1) + return false; + + gIndexPos=*(int*)ch; + S32Swap(gIndexPos); + gGhostIndexPos=gIndexPos; + ch+=sizeof(int); + + gLogPos=*(int*)ch; + S32Swap(gLogPos); + ch+=sizeof(int); + + gReplayOldFrameCount=*(int*)ch; + S32Swap(gReplayOldFrameCount); + ch+=sizeof(int); + + MemoryMove(gPacketLogIndex,ch,sizeof(tLogEntry)*gIndexPos); + for(int i=0;i +#include +#include +#include + +//needed for ForceFeedback.h +//----------------------------------------------------------------- +typedef OSStatus HRESULT; +typedef UInt32 IOByteCount; +typedef unsigned int io_service_t; +typedef unsigned int io_object_t; +#define S_OK ((HRESULT)0x00000000L) +//----------------------------------------------------------------- +#include +extern "C"{ +#include "ImmrHIDUtilAddOn.h" +} +#include "controls.h" +#include "config.h" +#include "error.h" +#include "gameframe.h" +#include "gamesystem.h" +#include "gametime.h" + +typedef struct{ + pRecDevice device; + int numElements; + pRecElement *elements; +} tHIDController; + +tHIDController *gControllers; + +int gInputHID=false; +int gNumControllers=0; + +int gIShockIIFFB=false; +//iS2F_DeviceRef_t giShockList[iS2F_MAX_ISHOCK2_NUM]; +int gIShockIIFFBBlock=0; + +int gFFB=false; +FFDeviceObjectReference gFFDeviceRef; +FFEffectObjectReference gGroundRumbleEffectRef = NULL; +FFEffectObjectReference gJoltEffectRef = NULL; +FFEffectObjectReference gFrictionEffectRef = NULL; +FFEffectObjectReference gAlignEffectRef = NULL; +FFEffectObjectReference gEngineEffectRef = NULL; + +DWORD dwAxes[2] = {FFJOFS_X,FFJOFS_Y}; +LONG lDirection[2] = {0,0}; + +extern int gAxisSteering; + +//returns true if the key with the key code k is pressed, false otherwise +short IsPressed(unsigned short k ) +{ + if(k<0||k>255) + return false; + + KeyMapByteArray km; + + GetKeys( *((KeyMap*)&km)); + return ( ( km[k>>3] >> (k & 7) ) & 1); +} + + +//Initialize controls +void ControlInit() +{ + if(HIDBuildDeviceList(NULL,NULL)) + { + gNumControllers=0; + pRecDevice device=HIDGetFirstDevice(); + do{ + //if(!(device->usagePage==kHIDPage_Unknown||(device->usagePage==kHIDPage_GenericDesktop&&(device->usage==kHIDUsage_GD_Mouse||device->usage==kHIDUsage_GD_Keyboard)))) + if(device->usagePage==kHIDPage_GenericDesktop&&(device->usage==kHIDUsage_GD_Joystick||device->usage==kHIDUsage_GD_GamePad))//||kHIDUsage_GD_Mouse)) + gNumControllers++; + device=HIDGetNextDevice(device); + }while(device); + + gControllers=(tHIDController*)NewPtr(sizeof(tHIDController)*gNumControllers); + + gNumControllers=0; + device=HIDGetFirstDevice(); + do{ +// if(!(device->usagePage==kHIDPage_Unknown||(device->usagePage==kHIDPage_GenericDesktop&&(device->usage==kHIDUsage_GD_Mouse||device->usage==kHIDUsage_GD_Keyboard)))) + if(device->usagePage==kHIDPage_GenericDesktop&&(device->usage==kHIDUsage_GD_Joystick||device->usage==kHIDUsage_GD_GamePad))//||kHIDUsage_GD_Mouse)) + { + io_service_t hidDeviceObject=AllocateHIDObjectFromRecDevice(device); + if(hidDeviceObject) + if(FFIsForceFeedback(hidDeviceObject)==FF_OK) + if(FFCreateDevice(hidDeviceObject,&gFFDeviceRef)==FF_OK) + gFFB=true; + + gControllers[gNumControllers].device=device; + gControllers[gNumControllers].numElements=HIDCountDeviceElements(gControllers[gNumControllers].device,kHIDElementTypeInput); + gControllers[gNumControllers].elements=(pRecElement*)NewPtr(sizeof(pRecElement)*gControllers[gNumControllers].numElements); + gControllers[gNumControllers].elements[0]=HIDGetFirstDeviceElement(gControllers[gNumControllers].device,kHIDElementTypeInput); + for(int j=1;jiShockFFB) + { + iS2F_JoltCmd_t joltCmd; + joltCmd.motorCmd.leftMotorMagnitude=lMag*10; + joltCmd.motorCmd.rightMotorMagnitude=rMag*10; + joltCmd.duration=duration*1000; + iS2F_SimpleJolt(giShockList[0],&joltCmd); + gIShockIIFFBBlock=gFrameCount+duration*kFPS; + }*/ + if(gFFB&&gAxisSteering&&gConfig->ffb) + { + float mag=(rMag+lMag)*0.5; + float offset=rMag-lMag; + + HRESULT hr; + FFEFFECT ffeffect; + FFPERIODIC ffperiodic; + FFENVELOPE ffenvelope; + + ffenvelope.dwSize = sizeof(ffenvelope); + ffenvelope.dwAttackLevel = 0; + ffenvelope.dwAttackTime = 0; /* Microseconds */ + ffenvelope.dwFadeLevel = 0; + ffenvelope.dwFadeTime = duration*FF_SECONDS; /* Microseconds */ + + ffperiodic.dwMagnitude = FF_FFNOMINALMAX*gConfig->ffbIntensity*mag; + ffperiodic.lOffset = FF_FFNOMINALMAX*gConfig->ffbIntensity*offset; + ffperiodic.dwPhase = 0; + ffperiodic.dwPeriod = 0.1*FF_SECONDS; // 10 Hz + + ffeffect.dwSize = sizeof(ffeffect); /* sizeof(FFEFFECT) */ + ffeffect.dwFlags = FFEFF_CARTESIAN; /* FFEFF_* */ + ffeffect.dwDuration = duration*FF_SECONDS; /* Microseconds */ + ffeffect.dwSamplePeriod = 1000; /* Microseconds */ + ffeffect.dwGain = FF_FFNOMINALMAX; + ffeffect.dwTriggerButton = FFEB_NOTRIGGER; /* or FFEB_NOTRIGGER */ + ffeffect.dwTriggerRepeatInterval = 0; /* Microseconds */ + ffeffect.cAxes = 1; /* Number of axes */ + ffeffect.rgdwAxes = dwAxes; /* Array of axes */ + ffeffect.rglDirection = lDirection; /* Array of directions */ + ffeffect.lpEnvelope = &ffenvelope; /* Optional */ + ffeffect.cbTypeSpecificParams = sizeof(ffperiodic); /* Size of params */ + ffeffect.lpvTypeSpecificParams = &ffperiodic; /* Pointer to params */ + ffeffect.dwStartDelay = 0; /* Microseconds */ + + if(gJoltEffectRef==NULL) + { + FFDeviceCreateEffect(gFFDeviceRef, kFFEffectType_Square_ID, &ffeffect, &gJoltEffectRef); + FFEffectDownload(gJoltEffectRef); + FFEffectStart(gJoltEffectRef, 1, 0); + } + else + { + FFEffectSetParameters(gJoltEffectRef,&ffeffect,FFEP_GAIN); + FFEffectStart(gJoltEffectRef, 1, 0); + } + } +#endif +} + + +void FFBSetGoundRumble(float velo, float rumble) +{ + if(gFFB&&gConfig->ffb) + { + if(!gAxisSteering) + rumble=0; + + velo/=60; + if(velo>1)velo=1; + + HRESULT hr; + FFEFFECT ffeffect; + FFPERIODIC ffperiodic; + + ffperiodic.dwMagnitude = FF_FFNOMINALMAX*gConfig->ffbIntensity*rumble*velo*0.5; + ffperiodic.lOffset = 0; + ffperiodic.dwPhase = 0; + if(velo) + ffperiodic.dwPeriod = FF_SECONDS*0.8/velo; + else + ffperiodic.dwPeriod = 0; + + ffeffect.dwSize = sizeof(ffeffect); /* sizeof(FFEFFECT) */ + ffeffect.dwFlags = FFEFF_CARTESIAN; /* FFEFF_* */ + ffeffect.dwDuration = FF_INFINITE; /* Microseconds */ + ffeffect.dwSamplePeriod = 1000; /* Microseconds */ + ffeffect.dwGain = FF_FFNOMINALMAX; + ffeffect.dwTriggerButton = FFEB_NOTRIGGER; /* or FFEB_NOTRIGGER */ + ffeffect.dwTriggerRepeatInterval = 0; /* Microseconds */ + ffeffect.cAxes = 1; /* Number of axes */ + ffeffect.rgdwAxes = dwAxes; /* Array of axes */ + ffeffect.rglDirection = lDirection; /* Array of directions */ + ffeffect.lpEnvelope = NULL; /* Optional */ + ffeffect.cbTypeSpecificParams = sizeof(ffperiodic); /* Size of params */ + ffeffect.lpvTypeSpecificParams = &ffperiodic; /* Pointer to params */ + ffeffect.dwStartDelay = 0; /* Microseconds */ + + if(gGroundRumbleEffectRef==NULL) + { + FFDeviceCreateEffect(gFFDeviceRef, kFFEffectType_Sine_ID, &ffeffect, &gGroundRumbleEffectRef); + FFEffectDownload(gGroundRumbleEffectRef); + FFEffectStart(gGroundRumbleEffectRef, 1, 0); + } + else + { + FFEffectSetParameters(gGroundRumbleEffectRef,&ffeffect,FFEP_TYPESPECIFICPARAMS+FFEP_GAIN); + FFEffectStatusFlag pFlags; + FFEffectGetEffectStatus(gGroundRumbleEffectRef,&pFlags); + if(!pFlags&FFEGES_PLAYING) + FFEffectStart(gGroundRumbleEffectRef, 1, 0); + } + } +} + +void FFBSetSteerResistance(float res,float alignment) +{ + if(gFFB&&gConfig->ffb) + { + if(!gAxisSteering) + { + res=0; + alignment=0; + } + + HRESULT hr; + FFEFFECT ffeffect; + FFCONDITION ffcondition; + + ffcondition.lOffset=0; + ffcondition.lPositiveCoefficient=FF_FFNOMINALMAX*gConfig->ffbIntensity*res; + ffcondition.lNegativeCoefficient=FF_FFNOMINALMAX*gConfig->ffbIntensity*res; + ffcondition.dwPositiveSaturation=FF_FFNOMINALMAX*gConfig->ffbIntensity*res; + ffcondition.dwNegativeSaturation=FF_FFNOMINALMAX*gConfig->ffbIntensity*res; + ffcondition.lDeadBand=0; + + ffeffect.dwSize = sizeof(ffeffect); /* sizeof(FFEFFECT) */ + ffeffect.dwFlags = FFEFF_CARTESIAN; /* FFEFF_* */ + ffeffect.dwDuration = FF_INFINITE; /* Microseconds */ + ffeffect.dwSamplePeriod = 1000; /* Microseconds */ + ffeffect.dwGain = FF_FFNOMINALMAX; + ffeffect.dwTriggerButton = FFEB_NOTRIGGER; /* or FFEB_NOTRIGGER */ + ffeffect.dwTriggerRepeatInterval = 0; /* Microseconds */ + ffeffect.cAxes = 1; /* Number of axes */ + ffeffect.rgdwAxes = dwAxes; /* Array of axes */ + ffeffect.rglDirection = lDirection; /* Array of directions */ + ffeffect.lpEnvelope = NULL; /* Optional */ + ffeffect.cbTypeSpecificParams = sizeof(ffcondition); /* Size of params */ + ffeffect.lpvTypeSpecificParams = &ffcondition; /* Pointer to params */ + ffeffect.dwStartDelay = 0; /* Microseconds */ + + if(gFrictionEffectRef==NULL) + { + FFDeviceCreateEffect(gFFDeviceRef, kFFEffectType_Friction_ID, &ffeffect, &gFrictionEffectRef); + FFEffectDownload(gFrictionEffectRef); + FFEffectStart(gFrictionEffectRef, 1, 0); + } + else + { + FFEffectSetParameters(gFrictionEffectRef,&ffeffect,FFEP_TYPESPECIFICPARAMS+FFEP_GAIN); + FFEffectStatusFlag pFlags; + FFEffectGetEffectStatus(gFrictionEffectRef,&pFlags); + if(!pFlags&FFEGES_PLAYING) + FFEffectStart(gFrictionEffectRef, 1, 0); + } + + + ffcondition.lOffset=0; + ffcondition.lPositiveCoefficient=FF_FFNOMINALMAX*gConfig->ffbIntensity*alignment; + ffcondition.lNegativeCoefficient=FF_FFNOMINALMAX*gConfig->ffbIntensity*alignment; + ffcondition.dwPositiveSaturation=FF_FFNOMINALMAX*gConfig->ffbIntensity*alignment; + ffcondition.dwNegativeSaturation=FF_FFNOMINALMAX*gConfig->ffbIntensity*alignment; + ffcondition.lDeadBand=0; + + if(gAlignEffectRef==NULL) + { + FFDeviceCreateEffect(gFFDeviceRef, kFFEffectType_Spring_ID, &ffeffect, &gAlignEffectRef); + FFEffectDownload(gAlignEffectRef); + FFEffectStart(gAlignEffectRef, 1, 0); + } + else + { + FFEffectSetParameters(gAlignEffectRef,&ffeffect,FFEP_TYPESPECIFICPARAMS+FFEP_GAIN); + FFEffectStatusFlag pFlags; + FFEffectGetEffectStatus(gAlignEffectRef,&pFlags); + if(!pFlags&FFEGES_PLAYING) + FFEffectStart(gAlignEffectRef, 1, 0); + } + } +} + + +void FFBiShockDirect(float lMag,float rMag) +{ +/* if(gIShockIIFFB&&gConfig->iShockFFB&&gFrameCount>gIShockIIFFBBlock) + { + iS2F_MotorCmd_t directCmd; + directCmd.leftMotorMagnitude=lMag*10; + directCmd.rightMotorMagnitude=rMag*10; + iS2F_SimpleDirectControl(giShockList[0],&directCmd); + }*/ +} + +void FFBStop() +{ +/* if(gIShockIIFFB&&gConfig->iShockFFB) + { + iS2F_MotorCmd_t directCmd; + directCmd.leftMotorMagnitude=0; + directCmd.rightMotorMagnitude=0; + iS2F_SimpleDirectControl(giShockList[0],&directCmd); + }*/ + if(gFFB&&gConfig->ffb) + { + FFEffectStop(gGroundRumbleEffectRef); + FFEffectStop(gJoltEffectRef); + FFEffectStop(gFrictionEffectRef); + FFEffectStop(gAlignEffectRef); + FFEffectStop(gEngineEffectRef); + + gGroundRumbleEffectRef = NULL; + gJoltEffectRef = NULL; + gFrictionEffectRef = NULL; + gAlignEffectRef = NULL; + gEngineEffectRef = NULL; + } +} + +//Dispose*/ Control structures +void ControlExit() +{ +/* if(gInputHID) + { + HIDReleaseDeviceList(); + TearDownHIDCFM(); + } +/* if(gIShockIIFFB) + { + FFBStop(); + iS2F_Final(); + }*/ +} + +void CalibrateAxis(int id) +{ + while(GetInterfaceKey(kInterfaceKeyReturn)||GetInterfaceKey(kInterfaceKeyEnter)); + int controller=-1; + for(int i=0;ivendorID==gConfig->axis[id].axisControllerID1&&gControllers[i].device->productID==gConfig->axis[id].axisControllerID2) + controller=i; + + int element=gConfig->axis[id].axisElementID; + + if(controller=0) + { + gConfig->axis[id].max=gConfig->axis[id].mid+1; + gConfig->axis[id].min=gConfig->axis[id].mid-1; + while(!(GetInterfaceKey(kInterfaceKeyReturn)||GetInterfaceKey(kInterfaceKeyEnter))) + { + SInt32 value=HIDGetElementValue(gControllers[controller].device,gControllers[controller].elements[element]); + if(valueaxis[id].min)gConfig->axis[id].min=value; + if(value>gConfig->axis[id].max)gConfig->axis[id].max=value; + } + } +} + + +#define kAxisMaxZone 0.9 + +//read an analogue axis +float GetAxisInput(int id) +{ + if(gConfig->axis[id].axisControllerID1==-1&&gConfig->axis[id].axisControllerID2==-1) + { + //mouse pos + tVector2 p=GetMousePos(); + if(gConfig->axis[id].axisElementID==1) + return p.x; + if(gConfig->axis[id].axisElementID==2) + return p.y; + } + if(gConfig->axis[id].axisControllerID1&&gInputHID){ + int controller=-1; + for(int i=0;ivendorID==gConfig->axis[id].axisControllerID1&&gControllers[i].device->productID==gConfig->axis[id].axisControllerID2) + controller=i; + + int element=gConfig->axis[id].axisElementID; + + if(controller=0) + if(element=0) + { + SInt32 value=HIDGetElementValue(gControllers[controller].device,gControllers[controller].elements[element]); + int min=gConfig->axis[id].mid-(gConfig->axis[id].mid-gConfig->axis[id].min)*kAxisMaxZone; + int max=gConfig->axis[id].mid-(gConfig->axis[id].mid-gConfig->axis[id].max)*kAxisMaxZone; + float ax; + if(id>=kInputThrottleAxis) + { + if(value<(gConfig->axis[id].max-gConfig->axis[id].min)*gConfig->axis[id].deadzone*0.5) + ax=0; + else + ax=(value-(gConfig->axis[id].max-gConfig->axis[id].min)*gConfig->axis[id].deadzone*0.5)/((gConfig->axis[id].max-min)*(1-0.5*gConfig->axis[id].deadzone)); + } + else + { + if(fabs(gConfig->axis[id].mid-value)<(gConfig->axis[id].max-gConfig->axis[id].min)*gConfig->axis[id].deadzone) + ax=0; + else if(value>gConfig->axis[id].mid) + ax=(value-(gConfig->axis[id].mid+(gConfig->axis[id].max-gConfig->axis[id].min)*gConfig->axis[id].deadzone))/((gConfig->axis[id].mid-max)*(1-0.5*gConfig->axis[id].deadzone)); + else + ax=(value-(gConfig->axis[id].mid-(gConfig->axis[id].max-gConfig->axis[id].min)*gConfig->axis[id].deadzone))/((min-gConfig->axis[id].mid)*(1-0.5*gConfig->axis[id].deadzone)); + } + if(ax>1)ax=1; + else if(ax<-1)ax=-1; + return ax; + } + } + return 0; +} + +//read a digital input +int GetButtonInput(int id) +{ + if(gInputChatMode) + return false; + //is the key pressed? + int key=gSystemSuspended?false:IsPressed(gConfig->keys[id].keyID); + //is the HID button pressed? + if(gConfig->axis[id].axisControllerID1==-1&&gConfig->axis[id].axisControllerID2==-1) + if(Button()) + key=true; + if(gConfig->keys[id].controllerID1&&gInputHID) + { + int controller=-1; + for(int i=0;ivendorID==gConfig->keys[id].controllerID1&&gControllers[i].device->productID==gConfig->keys[id].controllerID2) + controller=i; + + int element=gConfig->keys[id].elementID; + + if(controller=0) + if(element=0) + return key||HIDGetElementValue(gControllers[controller].device,gControllers[controller].elements[element]); + } + return key; +} + +int gInterfaceKeys[kInterfaceNumKeys]={0x00,0x7e,0x7d,0x7b,0x7c,0x31,0x24,0x4c,0x35,0x07,0x33,0x37,0x3a,0x01,0x0f,0x0c,0xff}; +int GetInterfaceKey(int id) +{ + if(id==kInterfaceMouseDown) + return gSystemSuspended?false:Button(); + else + return gSystemSuspended?false:IsPressed(gInterfaceKeys[id]); +} + +static void GetElementName (pRecDevice pDevice,pRecElement pElement, char *strElementName) +{ + if(!HIDGetElementNameFromVendorProductUsage (pDevice->vendorID, pDevice->productID, pElement->usagePage, pElement->usage,strElementName)) + { + // set name from vendor id/product id look up + HIDGetElementNameFromVendorProductCookie (pDevice->vendorID, pDevice->productID, (long) pElement->cookie, strElementName); + if (!*strElementName) { // if no name + char buffer[1024]; + HIDGetUsageName (pElement->usagePage, pElement->usage,buffer); + if (!*buffer) // if not usage + sprintf (strElementName, "Unknown"); + else + sprintf(strElementName,"%s %s",pDevice->product,buffer); + } + } +} + + +//used to user-configure input. if any key or HID button is pressed, +//this changes the according ID and Identifier string. +void GetInput(tKeyConfig *keyConfig) +{ + //FlushEvents(keyDownMask,0); + int key=-1; + do{ + for(int i=0;i<128;i++) + if(IsPressed(i)) + if(i!=0x35&&i!=0x33) + key=i; + else + { + if(i==0x33) + { + keyConfig->controllerID1=0; + keyConfig->controllerID2=0; + keyConfig->elementID=0; + strcpy(keyConfig->controllerIdentifier,""); + keyConfig->keyID=-1; + strcpy(keyConfig->identifier,""); + } + FlushEvents(keyDownMask|mDownMask,0); + FlushKeys(); + return; + } + if(gInputHID) + for(int j=0;jtype==kIOHIDElementTypeInput_Button) + if(HIDGetElementValue(gControllers[j].device,gControllers[j].elements[i])) + { + keyConfig->controllerID1=gControllers[j].device->vendorID; + keyConfig->controllerID2=gControllers[j].device->productID; + keyConfig->elementID=i; + char elementName[256]; + GetElementName(gControllers[j].device,gControllers[j].elements[i],elementName); +// sprintf(keyConfig->controllerIdentifier,"%s: %s",gControllers[j].device->product,elementName); + strcpy(keyConfig->controllerIdentifier,elementName); + return; + } + if(Button()&&!StillDown()) + { + keyConfig->controllerID1=-1; + keyConfig->controllerID2=-1; + keyConfig->elementID=1; + strcpy(keyConfig->controllerIdentifier,"Mouse Button"); + FlushEvents(keyDownMask|mDownMask,0); + FlushKeys(); + return; + } + }while(key==-1); + + int clear; + do{ + clear=true; + for(int i=0;i<128;i++) + if(IsPressed(i)) + clear=false; + }while(!clear); + + EventRecord evt; + Str255 str; + GetIndString(str,128,key+1); + int len=str[0]; + BlockMoveData(str+1,str,255); + str[len]='\0'; + + if(str[0]) + strcpy(keyConfig->identifier,(char*)str); + else + { + strcpy(keyConfig->identifier,"unknown key"); + while(WaitNextEvent(keyUpMask,&evt,1,nil)) + { + keyConfig->identifier[0]=evt.message&charCodeMask; + if(keyConfig->identifier[0]>='a'&&keyConfig->identifier[0]<='z') + keyConfig->identifier[0]+='A'-'a'; + keyConfig->identifier[1]='\0'; + } + } + + keyConfig->keyID=key; + + FlushEvents(keyDownMask|mDownMask,0); + FlushKeys(); +} + + +//used to user-configure input. if any HID axis is moved, +//this changes the according ID and Identifier string. +void GetAxis(tAxisConfig *axis,bool allowVendor) +{ + tVector2 basep=GetMousePos(); + float **values=(float**)NewPtr(sizeof(float)*gNumControllers); + for(int j=0;jtype!=kIOHIDElementTypeInput_Button) + values[j][i]=HIDGetElementValue(gControllers[j].device,gControllers[j].elements[i]); + } + + int key=-1; + do{ + long maxDiff=0; + for(int i=0;i<128;i++) + if(IsPressed(i)) + key=i; + if(key==0x33) + { + axis->axisControllerID1=0; + axis->axisControllerID2=0; + axis->axisElementID=0; + strcpy(axis->axisIdentifier,""); + } + for(int j=0;jusagePage!=kHIDPage_VendorDefinedStart||allowVendor) + if(gControllers[j].elements[i]->type!=kIOHIDElementTypeInput_Button) + if(abs(values[j][i]-HIDGetElementValue(gControllers[j].device,gControllers[j].elements[i]))>0) + { + maxDiff=abs(values[j][i]-HIDGetElementValue(gControllers[j].device,gControllers[j].elements[i])); + axis->axisControllerID1=gControllers[j].device->vendorID; + axis->axisControllerID2=gControllers[j].device->productID; + axis->axisElementID=i; + axis->min=gControllers[j].elements[i]->min; + axis->max=gControllers[j].elements[i]->max; + axis->mid=(axis->min+axis->max)/2; + char elementName[256]; + GetElementName(gControllers[j].device,gControllers[j].elements[i],elementName); + //sprintf(axis->axisIdentifier,"%s: %s",gControllers[j].device->product,elementName); + strcpy(axis->axisIdentifier,elementName); + + } + if(maxDiff) + { + for(int j=0;j0.1) + { + axis->axisControllerID1=-1; + axis->axisControllerID2=-1; + axis->axisElementID=fabs(p.x-basep.x)>fabs(p.y-basep.y)?1:2; + sprintf(axis->axisIdentifier,"Mouse %c Axis",'W'+axis->axisElementID); + + for(int j=0;jscreenXSize)+1,(2.0*p.v/gConfig->screenYSize)-1); +} \ No newline at end of file diff --git a/source/macerror.cpp b/source/macerror.cpp new file mode 100644 index 0000000..2aaeec0 --- /dev/null +++ b/source/macerror.cpp @@ -0,0 +1,116 @@ +#include +#ifndef __TARGET_TOOLAPP +#include "screen.h" +#endif +/* +typedef struct +{ + unsigned long fSaveSP,fSaveCR,fSaveLR,fResv0,fResv1,fSaveRTOC; +} tStackFrame; + +asm unsigned long GetCallersSP( void ) +{ + lwz r3,0(SP) + blr +} + +Str255 *FindRoutineName( unsigned long *codeAddress ) +{ + // look for the callers' "blr" instruction + // assume it's going to be within 8K instructions of the call site. + // this may or may not work for your code, worked for me. + + // the MacsBug name follows shortly after the 'blr' + // and at a fixed offset that I figured out empirically. + int i; + for( i=0; i<8000; i++) + { + if (codeAddress[i] == 0x4E800020) + { + // found the 'blr' + if (codeAddress[i+1] == 0x00000000) + { + return (Str255*) ( ((unsigned char*)&codeAddress[i])+21 ); + } + } + } + return nil; +} + +inline void GetCallerName(Str255 callerName) +{ + tStackFrame *frame = (tStackFrame*) GetCallersSP(); + unsigned long *address = (unsigned long*)frame->fSaveLR; + Str255 *name = FindRoutineName( address ); + if(name) + BlockMoveData(*name,callerName,(*name)[0]+1); + else + BlockMoveData("\p",callerName,20); +} +*/ + +void ShowAlert(char *str1, char *str2) +{ +#ifndef __TARGET_TOOLAPP + ScreenExit(); +#endif + AlertStdAlertParamRec alertParam={ + false,false,nil, + "\pExit", + nil, + nil, + kAlertStdAlertOKButton, + 0, + kWindowDefaultPosition}; + + Str255 pStr1,pStr2; + CopyCStringToPascal(str1,pStr1); + CopyCStringToPascal(str2,pStr2); + + short hit; + OSErr err=StandardAlert(kAlertStopAlert, + pStr1, + pStr2, + &alertParam, + &hit); + if(err)ExitToShell(); +} + +void FailWithErrorString(char *string) +{ + ShowAlert(string,""); + ExitToShell(); +} + +void PrintConsoleString(const char *fmt, ...) +{ + va_list ap; // Pointer To List Of Arguments + + if (fmt == NULL) // If There's No Text + return; // Do Nothing + + char error[256]; + va_start(ap, fmt); // Parses The String For Variables + vsprintf(error,fmt,ap); // And Converts Symbols To Actual Numbers + va_end(ap); // Results Are Stored In Text +/* + Str255 pStr; + CopyCStringToPascal(error,pStr); + DebugStr(pStr); */ + printf("%s\n",error); +} + +void HandleError(int code) +{ + if(code) + { + char str1[80],str2[80]; + char caller[80]="/p"; + //GetCallerName((unsigned char*)caller); + CopyPascalStringToC((unsigned char*)caller,caller); + sprintf(str1,"A fatal error has occurred!!"); + sprintf(str2,"Error ID=%d",code);// in function %s",code,caller); + ShowAlert(str1,str2); + ExitToShell(); + } +} \ No newline at end of file diff --git a/source/macfileio.cpp b/source/macfileio.cpp new file mode 100755 index 0000000..2ca643e --- /dev/null +++ b/source/macfileio.cpp @@ -0,0 +1,397 @@ +//macfileio.cpp +//mac-specific file handling code. + +#include +#include +#include "fileio.h" +#include "gamemem.h" +#include "error.h" +#include "config.h" +#include "screen.h" + +extern "C"{ +#include "compress.h" +} + +#define kFileNotPackaged -1 + +//System specific unique file locator +typedef struct{ + SInt16 vRefNum; + SInt16 unused; + SInt32 dirID; + Str255 pName; + unsigned int packageOffset,packageSize; +} tFileSystemLocator; + +//fills an FSSpecPtr with the file system information for +//the host application's Bundle directory +OSErr GetApplicationBundleFSSpec(FSSpecPtr theFSSpecPtr) +{ + FSRef location; + CFBundleRef refMainBundle = NULL; + CFURLRef refMainBundleURL = NULL; + refMainBundle = CFBundleGetMainBundle(); + refMainBundleURL = CFBundleCopyBundleURL (refMainBundle); + CFURLGetFSRef(refMainBundleURL,&location); + + return FSGetCatalogInfo(&location,kFSCatInfoNone,NULL,NULL,theFSSpecPtr,NULL); +} + +typedef struct{ + unsigned int offset,unPackedSize,packedSize; + char name[256]; +}tPackFileHeaderEntry; + +typedef struct{ + char magic[8]; + int numEntries; + int replaceFlag,unused2,unused3; + tPackFileHeaderEntry entries[]; +}tPackFileHeader; + +#define kMagicString "R3Dl1n3" + +tFileRef SimpleFileGetReference(char *name,tFileTableEntry *fileTable,int fileTableSize) +{ + for(int i=0;imagic,kMagicString)) + return; + int numEntries=EndianU32_BtoN(header->numEntries); + DisposePtr((Ptr)header); + + HandleError(SetFPos(ref,fsFromStart,0)); + + headerSize=sizeof(tPackFileHeader)+sizeof(tPackFileHeaderEntry)*numEntries; + header=(tPackFileHeader*)NewPtr(headerSize); + HandleError(FSRead(ref,&headerSize,header)); + + for(int i=0;ientries[i].name,fileTable,*fileTableSize); + int replaceable=false; + if(exists) + { + char *extension=FileGetExtension(i); + if(extension) + replaceable=(_stricmp(extension,kFileTypeCarDefinition)&&_stricmp(extension,kFileTypeMapDefinition)); + else + replaceable=true; + } + if(exists==-1||(header->replaceFlag&&replaceable)) + { + tFileRef ref; + if(exists==-1) + ref=(*fileTableSize)++; + else + ref=exists; + //add file record to file reference table + fileTable[ref].loaded=false; + fileTable[ref].parsed=false; + fileTable[ref].size=EndianU32_BtoN(header->entries[i].unPackedSize); + ((tFileSystemLocator*)&(fileTable[ref].fsLoc))->vRefNum=vRefNum; + ((tFileSystemLocator*)&(fileTable[ref].fsLoc))->dirID=dirID; + BlockMove(name,((tFileSystemLocator*)&(fileTable[ref].fsLoc))->pName,name[0]+1); + ((tFileSystemLocator*)&(fileTable[ref].fsLoc))->packageOffset=EndianU32_BtoN(header->entries[i].offset); + ((tFileSystemLocator*)&(fileTable[ref].fsLoc))->packageSize=EndianU32_BtoN(header->entries[i].packedSize); + strcpy(fileTable[ref].name,header->entries[i].name); + } + } + else + { + ShowAlert("Too many files in Plug-ins folder.","Please remove some files from your Plug-ins folder, and restart Redline."); + ExitToShell(); + } + DisposePtr((Ptr)header); + HandleError(FSClose(ref)); +} + +void AddFile(SInt16 vRefNum,SInt32 dirID,const Str255 name,tFileTableEntry *fileTable,int *fileTableSize,int maxSize) +{ + char cName[256]; + CopyPascalStringToC(name,cName); + if(*fileTableSizevRefNum=vRefNum; + ((tFileSystemLocator*)&(fileTable[*fileTableSize].fsLoc))->dirID=dirID; + BlockMove(name,((tFileSystemLocator*)&(fileTable[*fileTableSize].fsLoc))->pName,name[0]+1); + ((tFileSystemLocator*)&(fileTable[*fileTableSize].fsLoc))->packageOffset=kFileNotPackaged; + strcpy(fileTable[*fileTableSize].name,cName); + (*fileTableSize)++; + } + else + { + // if(_stricmp(cName,".DS_Store")) + // PrintConsoleString("File duplicate!: %s\n",cName); + } + else + { + ShowAlert("Too many files in Plug-ins folder.","Please remove some files from your Plug-ins folder, and restart Redline."); + ExitToShell(); + } +} + +#define IsPackage(i) (_stricmp(cName+strlen(cName)-strlen(kFileTypePackageFile),kFileTypePackageFile)==0) +//recursivly add all files in a directory and subdirectories +//to the file reference table +void IterateDirectoryLevel(SInt16 vRefNum,SInt32 dirID,tFileTableEntry *fileTable,int *fileTableSize,int maxSize) +{ + CInfoPBRec cinfo; + Str255 name; + for(int i=1; ;i++) + { + //Get info about the next file in the folder + cinfo.hFileInfo.ioVRefNum = vRefNum; + cinfo.hFileInfo.ioDirID = dirID; + cinfo.hFileInfo.ioNamePtr = name; + cinfo.hFileInfo.ioFDirIndex = i; + OSErr error=PBGetCatInfoSync(&cinfo); + + char cName[256]; + CopyPascalStringToC(name,cName); + + //no more files? + if(error==fnfErr)break; + HandleError(error); + + //is this a directory? + if(cinfo.hFileInfo.ioFlAttrib&kioFlAttribDirMask) + //recursively process subdirectory + IterateDirectoryLevel(vRefNum,cinfo.dirInfo.ioDrDirID,fileTable,fileTableSize,maxSize); + + #ifndef __PACKER + else if(IsPackage(cName)) + IteratePackage(vRefNum,dirID,name,fileTable,fileTableSize,maxSize); + #endif + else + AddFile(vRefNum,dirID,name,fileTable,fileTableSize,maxSize); + } +} + + +//declared in fileio.cpp +int FileTableCompare(const void *a,const void *b); + +SInt32 GetDirID(FSSpec *spec) +{ + CInfoPBRec cinfo; + cinfo.hFileInfo.ioVRefNum = spec->vRefNum; + cinfo.hFileInfo.ioDirID = spec->parID; + cinfo.hFileInfo.ioNamePtr = spec->name; + cinfo.hFileInfo.ioFDirIndex = 0; + OSErr error=PBGetCatInfoSync(&cinfo); + return cinfo.dirInfo.ioDrDirID; +} + +#define kPlugInDirName "Plug-Ins" +#define kPlugInDirPName "\pPlug-Ins" +//Initialize file reference table +void FileInitFileTable(tFileTableEntry *fileTable,int maxSize,int *fileTableSize,int reInit) +{ + FSSpec spec; + OSErr err; + + SInt16 vRefNum; //application's vRefNum + SInt32 dirID; //application's directory ID + + if(!reInit) + *fileTableSize=0; + + #ifdef __TARGET_PACKER + spec=packFolderFSS; + #else + #ifdef __TARGET_TOOLAPP + err=FSMakeFSSpec(0,0,"\p::::Redline.app",&spec); + if(err==fnfErr) + { + err=FSMakeFSSpec(0,0,"\p:::::Redline.app",&spec); + if(err==fnfErr) + { + ShowAlert("Can't find Redline","Please run the Tool Chest apps from the same folder Redline is in, or from the Tool Chest folder inside the Redline folder."); + ExitToShell(); + } + } + #else + //get FSSpec for application's Bundle + GetApplicationBundleFSSpec(&spec); + #endif + #endif + + //process file's in application bundle + //and subdirectories into file reference table + IterateDirectoryLevel(spec.vRefNum,GetDirID(&spec),fileTable,fileTableSize,maxSize); + + #ifndef __TARGET_PACKER + //process Plug-In folder if it exists + err=FSMakeFSSpec(spec.vRefNum,spec.parID,kPlugInDirPName,&spec); + if(err!=fnfErr) + IterateDirectoryLevel(spec.vRefNum,GetDirID(&spec),fileTable,fileTableSize,maxSize); + + //get reference to Preferences folder + SInt16 prefVRefNum; + SInt32 prefDirID; + HandleError(FindFolder(kOnAppropriateDisk,kPreferencesFolderType,kCreateFolder,&prefVRefNum,&prefDirID)); + + //see if Redline folder exists +/* FSSpec configFolderSpec; + err=FSMakeFSSpec(prefVRefNum,prefDirID,kConfigDirPName,&configFolderSpec); + SInt32 configFolderDirID; + //if not create it + if(err==fnfErr) + HandleError(FSpDirCreate(&configFolderSpec,smSystemScript,&configFolderDirID)); + //otherwise get the dirID + else + configFolderDirID=GetDirID(&configFolderSpec); +*/ + //see if config file exists + FSSpec configSpec; + err=FSMakeFSSpec(prefVRefNum,prefDirID,kConfigFilePName,&configSpec); + //if not create it + if(err==fnfErr) + FSpCreate(&configSpec,'????','????',smSystemScript); + + //Add Config folder to file table + AddFile(prefVRefNum,prefDirID,kConfigFilePName,fileTable,fileTableSize,maxSize); + #endif + + //and sort the filetable by file names + if(!reInit) + qsort(fileTable,*fileTableSize,sizeof(tFileTableEntry),&FileTableCompare); + + printf("%d files.\n",*fileTableSize); + + #ifndef __TARGET_PACKER + if(!FileGetSize(FileGetReference(kConfigFileName))) + { + tFileRef defaults=FileGetReference(kConfigDefault16Name); + int vram=GetVRAMSize(); + if(vram>16*1024*1024) + defaults=FileGetReference(kConfigDefault32Name); + if(vram>32*1024*1024) + defaults=FileGetReference(kConfigDefault64Name); + FileSetData(FileGetReference(kConfigFileName),FileGetDataPtr(defaults)); + } + #endif +} + + +//load a file's data +int FileLoadData(tFileRef fileRef) +{ + short ref; + HandleError(HOpenDF(((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->vRefNum + ,((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->dirID + ,((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->pName,fsCurPerm,&ref)); + long eof; + HandleError(GetEOF(ref,&eof)); + + if(((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->packageOffset==kFileNotPackaged) + { + gFileTable[fileRef].data=NewPtr(eof); + gFileTable[fileRef].size=eof; + HandleError(FSRead(ref,&eof,gFileTable[fileRef].data)); + } + else + { + long packedSize=((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->packageSize; + Ptr packedData=NewPtr(packedSize); + gFileTable[fileRef].data=NewPtr(gFileTable[fileRef].size); + unsigned long offset=((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->packageOffset; + HandleError(SetFPos(ref,fsFromStart,offset)); + HandleError(FSRead(ref,&packedSize,packedData)); + + + compress_identity *iden; + xcompress(COMPRESS_ACTION_IDENTITY,0,0,0,0,0,(ULONG*)&iden); + Ptr workBuffer=NewPtr(iden->memory); + + + UInt32 ignore; + xcompress(COMPRESS_ACTION_DECOMPRESS,(UBYTE*)workBuffer,(UBYTE*)packedData,packedSize,gFileTable[fileRef].size,(UBYTE*)gFileTable[fileRef].data,(ULONG*)&ignore); + + DisposePtr(workBuffer); + DisposePtr(packedData); + } + + HandleError(FSClose(ref)); + return true; +} + +void *FileGetPartialDataPtr(tFileRef fileRef,int offset,int size) +{ + if(((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->packageOffset==kFileNotPackaged) + { + short ref; + HandleError(HOpenDF(((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->vRefNum + ,((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->dirID + ,((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->pName,fsCurPerm,&ref)); + void *data=NewPtr(size); + HandleError(SetFPos(ref,fsFromStart,offset)); + long readSize=size; + HandleError(FSRead(ref,&readSize,data)); + HandleError(FSClose(ref)); + return data; + } + else + FailWithErrorString("Cannot get partial data from packaged file."); +} + +//write a file's data +void FileStoreData(tFileRef fileRef) +{ + if(((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->packageOffset==kFileNotPackaged) + { + short ref; + HandleError(HOpenDF(((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->vRefNum + ,((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->dirID + ,((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->pName,fsCurPerm,&ref)); + long eof=gFileTable[fileRef].size; + HandleError(SetEOF(ref,eof)); + HandleError(FSWrite(ref,&eof,gFileTable[fileRef].data)); + HandleError(FSClose(ref)); + } + else + FailWithErrorString("Cannot write to package file!"); +} + +void FileAppendData(tFileRef fileRef,void *data,int size) +{ + if(((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->packageOffset==kFileNotPackaged) + { + short ref; + HandleError(HOpenDF(((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->vRefNum + ,((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->dirID + ,((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->pName,fsCurPerm,&ref)); + long writeSize=size; + HandleError(SetFPos(ref,fsFromLEOF,0)); + HandleError(FSWrite(ref,&writeSize,data)); + HandleError(FSClose(ref)); + } + else + FailWithErrorString("Cannot write to package file!"); +} \ No newline at end of file diff --git a/source/maclocaltracker.cpp b/source/maclocaltracker.cpp new file mode 100644 index 0000000..61d9ddb --- /dev/null +++ b/source/maclocaltracker.cpp @@ -0,0 +1,490 @@ +#include +#include +#include +#include "error.h" +#include "tracker.h" +#include "gamemem.h" + +#define assert(condition) ((condition) ? ((void) 0) : PrintConsoleString("%s@%s:%s",#condition,__FILE__, __LINE__)) + +#define kMaxLocalGames 32 +int gNumLocalGames=0; +int gLocalGameListUpdate=0; +tGameListEntry gLocalGames[kMaxLocalGames]; + +static void +MyResolveCallback(CFNetServiceRef service, CFStreamError* error, void* info); + +#define kServiceType CFSTR("_redline._udp.") +#define kMyDefaultDomain CFSTR("local.") + +CFMutableArrayRef gServiceArrayRef; +CFMutableDictionaryRef gServiceDictionary; +CFNetServiceBrowserRef gServiceBrowserRef = NULL; +CFNetServiceRef gRegisteredService = NULL; +CFNetServiceRef gServiceBeingResolved = NULL; +UInt32 gNextNameIndexToResolve = 0; +CFStringRef gTextRecord; +Boolean gDone; +Boolean gContinue; +Boolean gResolve; +Boolean gBrowserTaskActive = FALSE; +Boolean gResolveProcessActive = FALSE; +Boolean gTaskRegistered = FALSE; + + +void InitLocalTracker() +{ + OSStatus err = noErr; + + gServiceDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, NULL); + if (gServiceDictionary == NULL) + err = coreFoundationUnknownErr; + + if (err == noErr) + { + gServiceArrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + if (gServiceDictionary == NULL) + { + err = coreFoundationUnknownErr; + } + } + + //if (err == noErr) + // err = LoadNetServicesForCFM(); + //return err; + +} + +static void +MyCancelRegistration() +{ + if(gRegisteredService != NULL) + { + CFNetServiceUnscheduleFromRunLoop(gRegisteredService, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); + CFNetServiceSetClient(gRegisteredService, NULL, NULL); + CFNetServiceCancel(gRegisteredService); + CFRelease(gRegisteredService); + gRegisteredService = NULL; + } +} + +static void +MyRegisterCallback(CFNetServiceRef theService, CFStreamError* error, void* info) +{ + if (error->domain == kCFStreamErrorDomainNetServices) + { + switch(error->error) + { + case kCFNetServicesErrorCollision: + + /* Somebody else on the network has registered a service with the same name and type + as the service we tried to register + */ + PrintConsoleString("A kCFNetServicesErrorCollision occured - will quit now\n"); + break; + + default: + /* + some other error occurred + */ + PrintConsoleString("Some other kCFNetServicesError occurred %d will quit now\n", error->error); + break; + } + // as an error occurred, clean up the CFNetServiceRef object + MyCancelRegistration(); + // you don't really need to quit, but the following boolean will cause the main to quit. + gDone = true; + } + + return; +} + +static void +MyCancelResolve() +{ + assert(gServiceBeingResolved != NULL); + + CFNetServiceUnscheduleFromRunLoop(gServiceBeingResolved, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); + CFNetServiceSetClient(gServiceBeingResolved, NULL, NULL); + CFNetServiceCancel(gServiceBeingResolved); + CFRelease(gServiceBeingResolved); + gServiceBeingResolved = NULL; + + return; +} + + +static void +MyResolveService(CFStringRef name, CFStringRef type, CFStringRef domain) +{ + CFNetServiceClientContext clientContext = { 0, NULL, NULL, NULL, NULL }; + CFStreamError error; + + assert(name != NULL); + assert(type != NULL); + assert(domain != NULL); + + if (gServiceBeingResolved) + { + /* This app only allows one resolve at a time, but CFNetServices places no restrictions on the number of + simultaneous resolves. Because most resolves will happen instantaneously after calling CFNetServiceResolve, + if we end up canceling a previous resolve, it's probably because the previous service became unreachable. */ + + PrintConsoleString("Resolve canceled\n"); + MyCancelResolve(); + } + + gServiceBeingResolved = CFNetServiceCreate(kCFAllocatorDefault, domain, type, name, 0); + assert(gServiceBeingResolved != NULL); + + CFNetServiceSetClient(gServiceBeingResolved, MyResolveCallback, &clientContext); + CFNetServiceScheduleWithRunLoop(gServiceBeingResolved, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); + + if (CFNetServiceResolve(gServiceBeingResolved, &error) == false) + { + // Something went wrong so lets clean up. + CFNetServiceUnscheduleFromRunLoop(gServiceBeingResolved, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); + CFNetServiceSetClient(gServiceBeingResolved, NULL, NULL); + CFRelease(gServiceBeingResolved); + gServiceBeingResolved = NULL; + + PrintConsoleString("CFNetServiceResolve returned (domain = %d, error = %ld)\n", error.domain, error.error); + } + + return; +} + +static void ContinueResolveProcess(void) +{ + CFStringRef serviceNameRef; + + if (gDone == true) + { + gResolveProcessActive = FALSE; + } + else + { + if (CFArrayGetCount(gServiceArrayRef) > gNextNameIndexToResolve) + { + (const void*)serviceNameRef = CFArrayGetValueAtIndex(gServiceArrayRef, gNextNameIndexToResolve++); + MyResolveService(serviceNameRef, kServiceType, kMyDefaultDomain); + } + else + gResolveProcessActive = FALSE; + } +} + + +#define U32Swap(value) ((value)=EndianU32_BtoN(value)) +#define U16Swap(value) ((value)=EndianU16_BtoN(value)) + +static void +MyResolveCallback(CFNetServiceRef service, CFStreamError* error, void* info) +{ + CFArrayRef addresses; + CFStringRef addressString; + CFStringRef serviceNameRef; + sockaddr *socketAddress; + sockaddr_in *socketAddress_in; + UInt32 inaddr; + UInt16 port; + char servicename[64]; + CFIndex namelen = sizeof(servicename); + UInt32 index; + + /* In situations where the service you're resolving is advertising on multiple interfaces, + like Ethernet and AirPort, your Resolve callback may be called twice, once for each IP address. + Chances are that both of these IP addresses represent the same service running on the same machine, + so we cancel the Resolve after getting the first callback because you only need one address to + connect to the service. However, it would also be possible that two different machines are + advertising the same service name, with one being on Ethernet and one on AirPort. In that + situation, one of the machines will be unreachable, or more likly, everytime we call Resolve, + we may connect to a different machine. The odds of this happening are extremely small. */ + assert(service != NULL); + + addresses = CFNetServiceGetAddressing(service); + assert(addresses != NULL); + serviceNameRef = CFNetServiceGetName(service); + assert(serviceNameRef != NULL); + if (CFStringGetCString(serviceNameRef, servicename, namelen, kCFStringEncodingMacRoman)) + { + servicename[namelen]='\0'; + for(int i=0;isa_family == AF_INET) + break; + } + + if (socketAddress) + { + switch(socketAddress->sa_family) + { + case AF_INET: + CFStringGetCString(serviceNameRef, servicename, namelen, kCFStringEncodingMacRoman); + socketAddress_in = (struct sockaddr_in *)socketAddress; + inaddr = *((UInt32*)(&socketAddress_in->sin_addr)); + port = *((UInt16*)(&socketAddress_in->sin_port)); + U32Swap(inaddr); + U16Swap(port); + sprintf(gLocalGames[i].host, "%d.%d.%d.%d:%d",inaddr>>24,(inaddr>>16)&0xFF,(inaddr>>8)&0xFF,inaddr&0xFF,port); + gLocalGames[i].loaded=true; + //PrintConsoleString("Got Host Info: %s",gLocalGames[i].host); + // Since we got an IPv4 address, this would be a good place to cancel the resolve. + gLocalGameListUpdate++; + MyCancelResolve(); + return; + + case AF_INET6: + PrintConsoleString("Resolver called with IPV6 address\n"); + /* If we got here, it probably means that the "addresses" array only had one sockaddr in it and it + was IPv6. We don't cancel the resolve just yet since this sample expects to print out the IPV4 address. + Just continue and the resolver will call back with the IPV4 address instance. */ + break; + } + } + } + } + + + // see if there are more entities to resolve + ContinueResolveProcess(); + + return; +} + + +static Boolean +MyRegisterService(CFStringRef name, CFStringRef type, CFStringRef domain, UInt32 port, CFStringRef txtRecord) +{ + CFNetServiceClientContext context = {0, NULL, NULL, NULL, NULL }; + CFStreamError error; + + assert(name != NULL); + assert(type != NULL); + assert(domain != NULL); + + /* As an alternative to specifying a "name" to register, you could use an empty string like + CFSTR(""), and that would cause the system to automatically substitute the "Computer Name" + from the Sharing preference panel. Another benefit of using an empty string is that the system + will automatically handle name collisions for us by appending a digit to the end of the name, + thus our Callback would never be called in the event of a name collision. + + Beginning with Mac OS X 10.2.4, using an empty string will even handle situations + where the user changes the "Computer Name" in the preference panel. */ + + gRegisteredService = CFNetServiceCreate(kCFAllocatorDefault, domain, type, name, port); + assert(gRegisteredService != NULL); + + // register the service asynchronously. + CFNetServiceSetClient(gRegisteredService, MyRegisterCallback, &context); + CFNetServiceScheduleWithRunLoop(gRegisteredService, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); + gTaskRegistered = CFNetServiceRegister(gRegisteredService, &error); + if (gTaskRegistered == FALSE) + { + // a problem happened with calling CFNetServiceRegister so unschedule the service from + // the runloop + CFNetServiceUnscheduleFromRunLoop(gRegisteredService, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); + CFNetServiceSetClient(gRegisteredService, NULL, NULL); + CFRelease(gRegisteredService); + gRegisteredService = NULL; + } + + return gTaskRegistered; +} + +static void StartResolveProcess(void) +{ + CFStringRef serviceNameRef; + + // Make sure that this process can't be entered multiple times + if (gResolveProcessActive == FALSE) + { + gResolveProcessActive = TRUE; + gNextNameIndexToResolve = 0; + if (CFArrayGetCount(gServiceArrayRef) > gNextNameIndexToResolve) + { + (const void*)serviceNameRef = CFArrayGetValueAtIndex(gServiceArrayRef, gNextNameIndexToResolve++); + MyResolveService(serviceNameRef, kServiceType, kMyDefaultDomain); + } + else + gResolveProcessActive = FALSE; + } +} + +static void +MyAddService(CFNetServiceRef service, CFOptionFlags flags) +{ + int referenceCount; + CFStringRef serviceNameRef; + char servicename[256]; + CFIndex namelen = sizeof(servicename); + Boolean addName = FALSE; + + /* You should do reference counting of each service because if the computer has two network + interfaces set up, like Ethernet and AirPort, you may get notified about the same service + twice, once from each interface. You probably don't want both items to be shown to the user. */ + + assert(service != NULL); + + serviceNameRef = CFNetServiceGetName(service); + + assert(serviceNameRef != NULL); + assert(gServiceDictionary != NULL); + + if (CFStringGetCString(serviceNameRef, servicename, namelen, kCFStringEncodingMacRoman)) + { + servicename[namelen]='\0'; + for(int i=0;i +#include +#include "screen.h" +#include "config.h" +#include "error.h" +#include "textures.h" + +AGLContext gOpenGLContext=NULL; + +AGLContext SetupAGL( AGLDrawable window,GLint *attrib,int width, int height) +{ + AGLPixelFormat format; + AGLContext context; + GLboolean ok; + GLint attrib2[256]; + + GDHandle screen; + if(window) + screen=GetGWorldDevice((GWorldPtr)window); + else + screen=DMGetFirstScreenDevice(true); + + if(window==NULL) + { + int i=0; + while(attrib[i]!=AGL_NONE) + attrib2[i++]=attrib[i]; + attrib2[i++]=AGL_FULLSCREEN; + //attrib2[i++]=AGL_NO_RECOVERY; + attrib2[i++]=AGL_NONE; + attrib=attrib2; + } + + // Choose an rgb pixel format + format = aglChoosePixelFormat( &screen, 1, attrib ); + if ( format == NULL ) + { + printf("aglChoosePixelFormat: %s\n",aglErrorString(aglGetError())); + return NULL; + } + // Create an AGL context + context = aglCreateContext( format, gOpenGLContext ); + if ( context == NULL ) + { + printf("aglCreateContext: %s\n",aglErrorString(aglGetError())); + return NULL; + } + + if(window){ + ok = aglSetDrawable(context,window); + if ( !ok ) + { + printf("aglSetDrawable: %s\n",aglErrorString(aglGetError())); + return NULL; + } + } + else{ + ok = aglSetFullScreen (context,width,height,75,0); + if ( !ok ) + { + ok = aglSetFullScreen (context,640,480,75,0); + if ( !ok ) + { + printf("aglSetFullScreen: %s\n",aglErrorString(aglGetError())); + return NULL; + } + } + } + + // Make the context the current context + ok = aglSetCurrentContext( context ); + if ( !ok ) + { + printf("aglSetCurrentContext: %s\n",aglErrorString(aglGetError())); + return NULL; + } + // The pixel format is no longer needed so get rid of it + aglDestroyPixelFormat( format ); + return context; +} + +//Initialize an OpenGL context using AGL +void InitGLContext() +{ + CGrafPtr theScreen=NULL; + + //is fullscreen mode enabled? + if(gConfig->fullscreen) + theScreen=NULL; + else + { + //get a Window Context + Rect boundsRect; + if(gConfig->windowY<40) + gConfig->windowY=40; + SetRect(&boundsRect,gConfig->windowX,gConfig->windowY,gConfig->windowX+gConfig->screenXSize,gConfig->windowY+gConfig->screenYSize); + WindowRef win=NewCWindow(0,&boundsRect,"\pRedline",true,0,(WindowRef)-1L,false,0); + if(!RectInRgn(&boundsRect,GetGrayRgn())) + MoveWindow(win,40,40,true); + theScreen=GetWindowPort(win); + SetPort(theScreen); + GetPortBounds(theScreen,&boundsRect); + Pattern black; + GetQDGlobalsBlack(&black); + FillRect(&boundsRect,&black); + HIWindowFlush(FrontWindow()); + } + + // Setup the OpenGL context + AGLContext ctx; + if(gConfig->fsaa) + { + GLint attrib[] = { AGL_RGBA, AGL_PIXEL_SIZE, gConfig->color32Bit?32:16,AGL_NO_RECOVERY,AGL_DOUBLEBUFFER, AGL_STENCIL_SIZE, 8,AGL_SAMPLE_BUFFERS_ARB,1,AGL_SAMPLES_ARB,gConfig->fsaa, AGL_NONE}; + // GLint attrib[] = { AGL_RGBA, AGL_PIXEL_SIZE, gConfig->color32Bit?32:16,AGL_DOUBLEBUFFER, AGL_STENCIL_SIZE, 8,AGL_SAMPLE_BUFFERS_ARB,1,AGL_SAMPLES_ARB,gConfig->fsaa, AGL_NONE}; + ctx=SetupAGL((AGLDrawable)theScreen,attrib,gConfig->screenXSize,gConfig->screenYSize); + } + else + { + GLint attrib[] = { AGL_RGBA, AGL_PIXEL_SIZE, gConfig->color32Bit?32:16,AGL_NO_RECOVERY,AGL_DOUBLEBUFFER, AGL_STENCIL_SIZE, 8, AGL_NONE}; + // GLint attrib[] = { AGL_RGBA, AGL_PIXEL_SIZE, gConfig->color32Bit?32:16,AGL_DOUBLEBUFFER, AGL_STENCIL_SIZE, 8, AGL_NONE}; + ctx=SetupAGL((AGLDrawable)theScreen,attrib,gConfig->screenXSize,gConfig->screenYSize); + } + if(!ctx) + { + if(gConfig->fullscreen) + { + gConfig->fullscreen=false; + InitGLContext(); + } + else + FailWithErrorString("Couldn't Create Screen"); + } + else + { + if(gOpenGLContext) + aglDestroyContext(gOpenGLContext); + gOpenGLContext=ctx; + } +} + +static int numberForKey( CFDictionaryRef desc, CFStringRef key ) +{ + CFNumberRef value; + int num = 0; + + if ( (value = (CFNumberRef)CFDictionaryGetValue(desc, key)) == NULL ) + return 0; + CFNumberGetValue(value, kCFNumberIntType, &num); + return num; +} + +void ScreenGetModes() +{ + CFArrayRef modeList; + CFIndex i, cnt; + + gVideoNumModes=0; + modeList = CGDisplayAvailableModes(kCGDirectMainDisplay); + if ( modeList == NULL ) + { + printf( "Display is invalid\n" ); + exit(1); + } + cnt = CFArrayGetCount(modeList); + for ( i = 0; i < cnt; ++i ) + { + CFDictionaryRef desc = (CFDictionaryRef)CFArrayGetValueAtIndex(modeList, i); + int depth=numberForKey(desc, kCGDisplayBitsPerPixel); + if(depth==32&&gVideoNumModes=60) + gVideoModes[j].freq=freq; + exists=true; + } + if(!exists) + { + gVideoModes[gVideoNumModes].height=height; + gVideoModes[gVideoNumModes].width=width; + gVideoModes[gVideoNumModes].freq=freq; + gVideoNumModes++; + } + } + } + + for(int i=0;i= dMaxVRAM) // find card with max VRAM + dMaxVRAM = dVRAM; // store max + } + info = aglNextRendererInfo(info); + inum++; + } +// aglDestroyRendererInfo(head_info); + + return dVRAM; +} + +int ScreenSupportsTextureCompression() +{ + static int result=-1; + if(result==-1) + { + char* extensions=(char*)glGetString(GL_EXTENSIONS); + result=strstr(extensions,"GL_EXT_texture_compression_s3tc")?true:false; + + long resp; + Gestalt(gestaltSystemVersion,&resp); + if(resp<0x00001030) + { + //GeForce Texture compression seems buggy in 10.2.8 + char* renderer=(char*)glGetString(GL_RENDERER); + if (strstr(renderer,"GeForce")) + result=false; + } + } + return result; +} + +int ScreenSupportsAnisotropicFiltering() +{ + static int result=-1; + if(result==-1) + { + char* extensions=(char*)glGetString(GL_EXTENSIONS); + result=strstr(extensions,"GL_EXT_texture_filter_anisotropic")?true:false; + + char* renderer=(char*)glGetString(GL_RENDERER); + //GeForce 5200 bug + if (strstr (renderer, "NV34MAP") || (strstr (renderer , "GeForce") && strstr (renderer, "5200"))) + result=false; + } + return result; +} + +int ScreenSupports3DTextures() +{ + static int result=-1; + if(result==-1) + { + /* + char* extensions=(char*)glGetString(GL_EXTENSIONS); + result=strstr(extensions,"GL_EXT_texture3D")?true:false; + */ + + result=true; //built-in in GL 1.2 or higher. + + char* renderer=(char*)glGetString(GL_RENDERER); + printf("Renderer: %s\n",renderer); + //GeForce 2 only has software 3d textures (? - according to unity) + if (strstr (renderer, "GeForce") && !(strstr (renderer , "TI") || strstr (renderer, "FX"))) + result=false; + //same with Rage128 + if (strstr (renderer, "Rage") && strstr (renderer, "128")) + result=false; + } + return result; +} + +int ScreenSupportsBlendColor() +{ + static int result=-1; + if(result==-1) + { + char* extensions=(char*)glGetString(GL_EXTENSIONS); + result=strstr(extensions,"GL_ARB_imaging")?true:false; + } + return result; +} + +int ScreenNoBigTextures() +{ + static int result=-1; + if(result==-1) + result=GetVRAMSize()<=1024*1024*8; + return result; +} + +int ScreenNoWindow() +{ + static int result=-1; + if(result==-1) + { + long resp; + Gestalt(gestaltSystemVersion,&resp); + result=(resp<0x00001030); + } + return result; +} \ No newline at end of file diff --git a/source/macsystem.cpp b/source/macsystem.cpp new file mode 100644 index 0000000..93db043 --- /dev/null +++ b/source/macsystem.cpp @@ -0,0 +1,923 @@ +#include +#include +#include +#include +#include +#include + +#include +#include /* new for v7 */ +#include /* new for v7 */ + +#include "controls.h" +#include "initexit.h" +#include "config.h" +#include "screen.h" +#include "textures.h" +#include "renderframe.h" +#include "environment.h" +#include "gameinitexit.h" +#include "gametime.h" +#include "gameframe.h" +#include "error.h" +#include "network.h" +#include "interfaceutil.h" +#include "models.h" +#include "text.h" +#include "GetPID.h" +#include "gamesound.h" +#include "reg_tool_3.h" + + +int gSystemSuspended=false; +int gInGame=false; +pthread_mutex_t gASMutex; + +// http://developer.apple.com/qa/qa2001/qa1111.html +// Creates an AppleEvent with one text parameter. We leave it up to the AppleScript +// to further parse the text parameter into potentially more parameters. +static OSStatus CreateMessageEvent( AppleEvent *theEvent, char *parameter ) +{ + OSStatus err; + ProcessSerialNumber psn = {0, kCurrentProcess}; + + err = AEBuildAppleEvent( 'ascr', kASSubroutineEvent, typeProcessSerialNumber, (Ptr) &psn, sizeof(psn), kAutoGenerateReturnID, kAnyTransactionID, + theEvent, + NULL, + "'----':[TEXT(@)]," // One TEXT pointer parameter + "'snam':TEXT(@)", // The keyASSubroutineName ('snam') parameter must contain the name of the subroutine that is being called with every letter converted to lowercase. For example, if name of the subroutine in your script is "GetDocumentSize", then the string provided in the keyASSubroutineName parameter should be "getdocumentsize". + parameter, "applescriptentry"); // The entry routine whithin the AppleScript + + return( err ); +} + +/***************************************************** +* +* ExecuteCompiledAppleScriptEvent( AEDesc *scriptData, AppleEvent *theEvent, AEDesc *resultData ) +* +* Purpose: Generic routine to execute our AppleScriptEvent, passing parameters to an +* AppleScript running inside my application +* +* Notes: http://developer.apple.com/qa/qa2001/qa1111.html +* +* Inputs: scriptData - Reference to the AppleScript to be executed +* theEvent - text parameter to our AppleScript as an AppleEvent +* resultData - result from script +* +* Returns: OSStatus - error code (0 == no error) +*/ +typedef struct{ + int inited; + ComponentInstance theComponent; + OSAID contextID; +} tScriptData; + +OSStatus ExecuteCompiledAppleScriptEvent( AEDesc *scriptData, AppleEvent *theEvent, AEDesc *resultData,tScriptData *scriptStore) +{ + OSStatus err; + ComponentInstance theComponent = NULL; + OSAID contextID = kOSANullScript; + OSAID resultID = kOSANullScript; + int inited=false; + + if(scriptStore) + if(scriptStore->inited) + { + theComponent=scriptStore->theComponent; + contextID=scriptStore->contextID; + inited=true; + } + if(!inited) + { + theComponent = OpenDefaultComponent( kOSAComponentType, typeAppleScript ); // Open the scripting component + if ( theComponent == NULL ) { err = paramErr; goto Bail; } + + err = OSALoad( theComponent, scriptData, kOSAModeNull, &contextID ); // Compile the script into a new context + require_noerr( err, Bail ); + } + + err = OSAExecuteEvent( theComponent, theEvent, contextID, kOSAModeNull, &resultID ); // Run the script + if ( resultData != NULL ) // Collect the results - if any + { + AECreateDesc( typeNull, NULL, 0, resultData ); + /*if ( err == errOSAScriptError ) + OSAScriptError( theComponent, kOSAErrorMessage, typeChar, resultData ); + else*/ if ( (err == noErr) && (resultID != kOSANullScript) ) + OSADisplay(theComponent, resultID, typeChar, kOSAModeNull, resultData); + } + +Bail: + if ( resultID != kOSANullScript ) OSADispose( theComponent, resultID ); + if(!scriptStore) + { + if ( contextID != kOSANullScript ) OSADispose( theComponent, contextID ); + if ( theComponent != NULL ) CloseComponent( theComponent ); + } + else + { + scriptStore->inited=true; + scriptStore->theComponent=theComponent; + scriptStore->contextID=contextID; + } + return( err ); +} + +/***************************************************** +* +* RunAppleScript( FSRef *scriptFSRef, char *textParameter ) +* +* Purpose: Runs an AppleScript with one text parameter as input. +* CreateMessageEvent, and therefore RunAppleScript, assumes the AppleScript has a +* subroutine entry titled "applescriptentry" and accepts one TEXT parameter. +* +* Inputs: scriptFSRef - FSRef to our AppleScript +* textParameter - text parameter to our AppleScript +* +* Returns: OSStatus - error code (0 == no error) +*/ +static OSStatus RunAppleScript( FSRef *scriptFSRef, char *textParameter, char *textReply,int textsize,tScriptData *d) +{ + OSStatus err; + AppleEvent aeParameter; + AEDesc scriptData; + short refNum; + FSCatalogInfo catalogInfo; + Handle h = NULL; + + pthread_mutex_lock(&gASMutex); + + refNum = FSOpenResFile( scriptFSRef, fsRdPerm ); // Older (Mac OS 8/9) scripts store their data in the 'scpt' (1) resource + if ( refNum != -1 ) + { + h = Get1IndResource( 'scpt', 1 ); + if( h != NULL ) DetachResource( h ); // Detach the handle before closing the resource + CloseResFile( refNum ); + } + if ( h == NULL ) + { + err = FSGetCatalogInfo( scriptFSRef, kFSCatInfoDataSizes, &catalogInfo, NULL, NULL, NULL ); // Get the size of the script + require_noerr( err, Bail ); + + err = FSOpenFork( scriptFSRef, 0, NULL, fsRdPerm, &refNum ); // Open the data fork read only + require_noerr( err, Bail ); + + h = NewHandle( catalogInfo.dataLogicalSize ); + err = FSReadFork( refNum, fsFromStart, 0, catalogInfo.dataLogicalSize, *h, NULL ); // Read the script into our handle + (void) FSCloseFork( refNum ); // Close the file reference + } + + err = CreateMessageEvent( &aeParameter, textParameter ); // Create the AppleEvent, and use the Apple event to call the script's subroutine + require_noerr( err, Bail ); + + err = AECreateDesc( typeOSAGenericStorage, *h, GetHandleSize(h), &scriptData ); // Load the compiled script into an AEDesc of type typeOSAGenericStorage + require_noerr( err, Bail ); + + AEDesc result; + err = ExecuteCompiledAppleScriptEvent( &scriptData, &aeParameter, &result,d); // "Generic" routine to execute our AppleScript + if(textReply) + { + int size=AEGetDescDataSize(&result); + AEGetDescData(&result,textReply,textsize-1); + textReply[textsize]='\0'; + if(sizenetwork) + PauseGame(); + } + return noErr; +} + +#define kMaxURLSize 256 + +pascal OSErr myGURL(const AppleEvent *theAE,AppleEvent *reply,long refCon) +{ + DescType type; + char theURL[kMaxURLSize]; + Size urlSize; + HandleError(AEGetParamPtr(theAE,keyDirectObject,typeChar,&type,theURL,kMaxURLSize,&urlSize)); + + if(type!=typeChar) + return paramErr; + + if(urlSize>=kMaxURLSize) + return paramErr; + + theURL[urlSize]='\0'; + + //PrintConsoleString("Opening URL \"%s\"....",theURL); + + if(theURL[strlen(theURL)-1]=='>') + theURL[strlen(theURL)-1]='\0'; + if(theURL[strlen(theURL)-1]=='/') + theURL[strlen(theURL)-1]='\0'; + sscanf(theURL,"<%s",theURL); + sscanf(theURL,"URL:%s",theURL); + sscanf(theURL,"redline://%s",gJoinHost); + + gJoinFlag=true; + gGameEnd=kEndGameNoReplay; + + return noErr; +} + +void InitAE() +{ + AEInstallEventHandler(kCoreEventClass,kAEQuitApplication,NewAEEventHandlerUPP(&myQUIT),0,false); + AEInstallEventHandler(kInternetEventClass,kAEGetURL,NewAEEventHandlerUPP(&myGURL),0,false); +} + +//#define kBetaExpiration (3232976085+4000000) +#define kBetaExpiration (0) + +void* ITunesPollThread(void * arg) +{ + while(1){ + ITunesGetStatus(); + sleep(1); + } +} + +void InitITunesNotifications(); + +void SystemInit() +{ + long resp; + short hit; + + AlertStdAlertParamRec alertParam={ + false,false,nil, + "\pExit", + nil, + nil, + kAlertStdAlertOKButton, + 0, + kWindowDefaultPosition}; + + HandleError(Gestalt(gestaltSystemVersion,&resp)); + if(resp<0x00001020) + { + StandardAlert(kAlertStopAlert, + "\pMac OS 10.2 or higher is required.", + "\p", + &alertParam, + &hit); + ExitToShell(); + } + + unsigned long dateTime; + GetDateTime(&dateTime); + + //PrintConsoleString("%u",dateTime); + if(kBetaExpiration&&dateTime>kBetaExpiration) + { + StandardAlert(kAlertStopAlert, + "\pThis beta of Redline has expired.", + "\p", + &alertParam, + &hit); + ExitToShell(); + } + + InitAE(); + + ITunesGetStatus(); + InitITunesNotifications(); + + pthread_mutex_init(&gASMutex,0); + //pthread_t thread; + //pthread_create(&thread,NULL,ITunesPollThread,NULL); + // CallOmniEnableFloatExceptions(); +} + +#define kInputBufferSize 16 +UInt32 gInputBuffer[kInputBufferSize]; +int gInputBufferPos=0; +float gLastCursorHideTime; +extern int gInterfaceKeys[kInterfaceNumKeys]; +UInt32 gLastSwitch=0; +int iTunesPress=false; +int gSystemInstalling=false; +float gInstallStartTime=0; + + +void SystemPoll(int inGame) +{ + EventRecord evt; + gInGame=inGame; + while(WaitNextEvent(everyEvent,&evt,0,nil)) + { + switch(evt.what) + { + case mouseDown: + WindowPtr win; + switch(FindWindow(evt.where,&win)) + { + case inDrag: + { + Rect dragRect={0,0,32767,32767}; + DragWindow(win,evt.where,&dragRect); + Point pt={0,0}; + LocalToGlobal(&pt); + gConfig->windowX=pt.h; + gConfig->windowY=pt.v; + } + break; + case inMenuBar: + MenuSelect(evt.where); + break; + case inContent: + if(evt.when>gLastSwitch+20) + if(gInputBufferPos>24)==suspendResumeMessage) + { + gSystemSuspended=!(evt.message&resumeFlag); + if(!gSystemSuspended) + BringToFront(FrontWindow()); + if(inGame) + { + if(gSystemSuspended&&inGame) + if((!gGameInfo->network)&&(!gReplay)) + PauseGame(); + } + else + if(gSystemSuspended) + PauseGame(); + else + UnPauseGame(); + gLastSwitch=evt.when; + if(!gPaused) + SoundReInit(); + } + break; + case kHighLevelEvent : + AEProcessAppleEvent(&evt); + break; + case keyDown: + case autoKey: + if(evt.modifiers&cmdKey) + switch(evt.message&charCodeMask) + { + case 'f': + if(!ScreenNoWindow()) + { + float pauseTime=TimeGetSeconds(); + gConfig->fullscreen=!gConfig->fullscreen; + + ScreenReInit(); + if(inGame) + { + gClipEnable=false; + RenderFrame(false); + gClipEnable=true; + + if(!gPaused&&!gGameInfo->network) + gStartTime+=TimeGetSeconds()-pauseTime; + } + } + break; + /* case 'q': + if((!inGame)||gInputEscMode==kInputEscModeNormal) + Exit(); + break;*/ + } + else + if(gInputBufferPosgLastCursorHideTime+1&&!gSystemInstalling) + { + gLastCursorHideTime=TimeGetSeconds(); + if(gConfig->fullscreen) + HideCursor(); + else + ObscureCursor(); + } + + if(((!gInterfaceType||GetInterfaceKey(kInterfaceKeyCmd))||inGame)&&!gSystemSuspended) + { + pthread_t thread; + if(GetButtonInput(kInputITunesNext)) + { + if(!iTunesPress) +// ITunesNext(NULL); + pthread_create(&thread,NULL,ITunesNext,NULL); + iTunesPress=true; + } + else if(GetButtonInput(kInputITunesPrev)) + { + if(!iTunesPress) +// ITunesPrev(NULL); + pthread_create(&thread,NULL,ITunesPrev,NULL); + iTunesPress=true; + } + else if(GetButtonInput(kInputITunesPlay)) + { + if(!iTunesPress) +// ITunesPlay(NULL); + pthread_create(&thread,NULL,ITunesPlay,NULL); + iTunesPress=true; + } + else + iTunesPress=false; + } + + RT3_Idle(); + NetworkIdle(); + SystemIdle(); +} + +void SystemYieldTime(float till) +{ + while(TimeGetSeconds()>8==gInterfaceKeys[i]) + *key=i; + if(gInputBuffer[gInputBufferPos]&charCodeMask) + return gInputBuffer[gInputBufferPos]&charCodeMask; + else + return 0xff; + } + return 0; +} + +char PeekKeyInput(int *key) +{ + if(key) + *key=kInterfaceKeyNone; + if(gInputBufferPos) + { + if(key) + for(int i=kInterfaceKeyUp;i>8==gInterfaceKeys[i]) + *key=i; + if(gInputBuffer[gInputBufferPos-1]&charCodeMask) + return gInputBuffer[gInputBufferPos-1]&charCodeMask; + else + return 0xff; + } + return 0; +} + +float TimeGetSeconds() +{ + static UInt64 startTime=0; + if(startTime==0) + Microseconds((UnsignedWide*)&startTime); + UInt64 w; + Microseconds((UnsignedWide*)&w); + return (float)((double)(w-startTime)*(double)0.000001); +} + +size_t my_write_func(void *ptr, size_t size, size_t nmemb, FILE *stream) +{ + return fwrite(ptr, size, nmemb, stream); +} + +size_t my_read_func(void *ptr, size_t size, size_t nmemb, FILE *stream) +{ + return fread(ptr, size, nmemb, stream); +} + +int my_progress_func(void *v, + double t, /* dltotal */ + double d, /* dlnow */ + double ultotal, + double ulnow) +{ + char status[1024]; + float time=TimeGetSeconds()-gInstallStartTime; + if(d>0) + { + time*=(t-d)/d; + sprintf(status,"%3.1f of %3.1fMB - about %d:%02d:%02d remaining",d/(1024*1024),t/(1024*1024),(int)(time/3600),((int)(time/60))%60,((int)time)%60); + } + else + sprintf(status,"%3.1f of %3.1fMB"); + + InterfaceDrawStatusBar("Downloading new version",status,d/t); + SystemPoll(false); + return 0; +} + +int CurlGetFile(char *url,char *filename,char *failStr) +{ +#ifndef __TARGET_TOOLAPP + CURL *curl; + CURLcode res; + FILE *outfile; + if(!curl_easy_init) + { + strcpy(failStr,"libcurl not available"); + return false; + } + + curl = curl_easy_init(); + if(curl) + { + outfile = fopen(filename, "w"); + if(!outfile) + { + strcpy(failStr,"Couldn't create file.\nMake sure that Redline and the Redline directory is writable and that you have free space on your disk."); + return false; + } + gSystemInstalling=true; + gInstallStartTime=TimeGetSeconds(); + + res = curl_easy_setopt(curl, CURLOPT_URL, url); + if(res==CURLE_OK) res = curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile); + if(res==CURLE_OK) res = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_write_func); + if(res==CURLE_OK) res = curl_easy_setopt(curl, CURLOPT_READFUNCTION, my_read_func); + if(res==CURLE_OK) res = curl_easy_setopt(curl, CURLOPT_NOPROGRESS, FALSE); + if(res==CURLE_OK) res = curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, my_progress_func); + + if(res==CURLE_OK) res = curl_easy_perform(curl); + + fclose(outfile); + /* always cleanup */ + curl_easy_cleanup(curl); + } + switch(res) + { + case CURLE_OK: break; + case 22: strcpy(failStr,"curl: HTTP error"); break; + case CURLE_WRITE_ERROR: strcpy(failStr,"curl: Error writing to disk"); break; + case CURLE_URL_MALFORMAT: strcpy(failStr,"curl: Invalid Update URL"); break; + case CURLE_COULDNT_RESOLVE_HOST: strcpy(failStr,"curl: Couldn't resolve host"); break; + case CURLE_COULDNT_CONNECT: strcpy(failStr,"curl: Connection failed"); break; + case CURLE_OPERATION_TIMEOUTED: strcpy(failStr,"curl: Connection timed out"); break; + case CURLE_RECV_ERROR: strcpy(failStr,"curl: Error receiving data"); break; + default: strcpy(failStr,"curl: Error downloading URL"); break; + } + return res==CURLE_OK; +#endif +} + +void SystemExit() +{ + if(gSystemInstalling) + system("rm redlineupdatearchive.zip"); +} + +int AutoUpdateRedline(char *updateURL,char *failStr) +{ + CFBundleRef refMainBundle = CFBundleGetMainBundle(); + if (!refMainBundle) + { + sprintf(failStr,"Couldn't get Redline bundle location"); + return false; + } + // create a URL to the app bundle + CFURLRef refMainBundleURL = CFBundleCopyBundleURL (refMainBundle); + if(!refMainBundleURL) + { + sprintf(failStr,"Couldn't get Redline bundle location"); + return false; + } + + char path[512]; + if(!CFURLGetFileSystemRepresentation(refMainBundleURL,true,(UInt8*)path,512)) + { + sprintf(failStr,"Couldn't get Redline bundle location"); + return false; + } + +// char cmd[512]; + sprintf(path,"%s/../",path); + chdir(path); + system("pwd"); +// printf(cmd); +// system(cmd); + + int retval=true; + InitCursor(); + if(!CurlGetFile(updateURL,"redlineupdatearchive.zip",failStr)) retval=false; + InterfaceDrawStrings("Installing new version","Please wait a moment...",-1); + if(retval) + if(system("unzip -o redlineupdatearchive.zip")) + { + retval=false; + sprintf(failStr,"Couldn't unzip update archive.\nMake sure that Redline and the Redline directory is writable and that you have free space on your disk."); + } + if(system("rm redlineupdatearchive.zip")) retval=false; + gSystemInstalling=false; + return retval; +} \ No newline at end of file diff --git a/source/mactexturesimport.cpp b/source/mactexturesimport.cpp new file mode 100644 index 0000000..873705d --- /dev/null +++ b/source/mactexturesimport.cpp @@ -0,0 +1,107 @@ +//mactexturesimport.cpp +//mac-specific (or rather "QuickTime-Specific") code to load textures + +#include +#include +#include +#include "fileio.h" +#include "error.h" +#include "gamemem.h" +#include "texturesimport.h" +#include "network.h" + +typedef struct{ + UInt8 r,g,b,a; +}tRGBAColor; + +typedef struct{ + UInt8 a,r,g,b; +}tARGBColor; + +void *TexturesLoadImportBuffer(tFileRef tex,int *xSize,int *ySize) +{ + ComponentInstance gi; + int gotImage=false; + char *fileExtension=FileGetExtension(tex); + + if(!_stricmp(fileExtension,kFileTypeImageURL)){ + tImageURL *url=(tImageURL*)FileGetParsedDataPtr(tex,kParserTypeImageURL,sizeof(tImageURL)); + Handle dataRef=NewHandle(strlen(url->url)+1); + strcpy(*dataRef,url->url); + + if(!gInternetAvailable) + tex=url->offlineImage; + else if(GetGraphicsImporterForDataRef(dataRef,URLDataHandlerSubType,&gi)!=noErr) + tex=url->offlineImage; + else + gotImage=true; + } + if(!gotImage) + { + void *textureData=FileGetDataPtr(tex); + Handle dataHandle,dataRef; + int size=FileGetSize(tex); + + //Get a Handle with the texture file data in it + PtrToHand(textureData,&dataHandle,FileGetSize(tex)); + + char *name=FileGetName(tex); + unsigned char len=strlen(name); + + //create a new data reference + dataRef=NewHandle(sizeof(Handle)+len+1); + *((Handle*)(*dataRef))=dataHandle; + BlockMoveData(name,*dataRef+sizeof(Handle)+1,len); + *(unsigned char*)(*dataRef+sizeof(Handle))=len; + HandleError(GetGraphicsImporterForDataRef(dataRef,HandleDataHandlerSubType,&gi)); + } + +// GWorldPtr oldGW; +// GDHandle oldGD; +// GetGWorld(&oldGW,&oldGD); + + //get image bounds + Rect bounds; + HandleError(GraphicsImportGetNaturalBounds(gi,&bounds)); + *xSize=bounds.right-bounds.left; + *ySize=bounds.bottom-bounds.top; + + //create a buffer to hold the decompressed pixel data + int rowBytes=*xSize*4; + void *imageBuffer=MemoryAllocateBlock(*ySize*rowBytes); + + //create a GWorld structure for the pixel buffer + GWorldPtr imageGW; + QTNewGWorldFromPtr(&imageGW,k32ARGBPixelFormat,&bounds,nil,nil,0,imageBuffer,rowBytes); + + //Set up graphics importer + HandleError(GraphicsImportSetGWorld(gi,imageGW,nil)); + HandleError(GraphicsImportSetQuality(gi,codecLosslessQuality)); + + //decompress the image to the GWorld + LockPixels(GetGWorldPixMap(imageGW)); + HandleError(GraphicsImportDraw(gi)); + UnlockPixels(GetGWorldPixMap(imageGW)); + + //convert ARGB to RGBA + for(int i=0;i<*ySize*rowBytes/4;i++) + { + tARGBColor buf=*(((tARGBColor*)imageBuffer)+i); + tRGBAColor c; + c.a=buf.a; + c.r=buf.r; + c.g=buf.g; + c.b=buf.b; + *(((tRGBAColor*)imageBuffer)+i)=c; + } + + //re-set the GWorld to use. +// SetGWorld(oldGW,oldGD); + + //dispose our structures. + DisposeGWorld(imageGW); + CloseComponent(gi); + + return imageBuffer; +} + diff --git a/source/main.cpp b/source/main.cpp new file mode 100755 index 0000000..98b6bd9 --- /dev/null +++ b/source/main.cpp @@ -0,0 +1,12 @@ +//main.cpp + +#include "initexit.h" +#include "interface.h" + +int main() +{ + Init(); + InterfaceMainLoop(); + Exit(); + return 0; +} \ No newline at end of file diff --git a/source/mapselection.cpp b/source/mapselection.cpp new file mode 100644 index 0000000..5f1cc9d --- /dev/null +++ b/source/mapselection.cpp @@ -0,0 +1,362 @@ +//mapselection.cpp +//let the user select a map + +#include +#include +#include "gamemem.h" +#include "fileio.h" +#include "gameinitexit.h" +#include "parser.h" +#include "config.h" +#include "environment.h" +#include "interfaceutil.h" +#include "mapselection.h" +#include "reg_tool_3.h" +#include "controls.h" + +#define kMaxMaps 1024 +#define kMaxEnvironments 64 + +char *StripName(char *aName) +{ + while(*aName==' '||*aName=='\t')aName++; + while(*aName=='\255') + { + aName++; + while(*aName!='\255'&&*aName!=0) + aName++; + if(*aName=='\255') + aName++; + while(*aName==' '||*aName=='\t')aName++; + } + return aName; +} + +int CompareMaps(const void *a,const void *b) +{ + tMapInfo *mapa=(tMapInfo*)FileGetParsedDataPtr(*(tFileRef*)a,kParserTypeMapInfoDesc,sizeof(tMapInfo)); + tMapInfo *mapb=(tMapInfo*)FileGetParsedDataPtr(*(tFileRef*)b,kParserTypeMapInfoDesc,sizeof(tMapInfo)); + return _stricmp(StripName(mapa->name),StripName(mapb->name)); +} + +//get a list of available map files +void GetAvailableMaps(int *availableMaps,int *mapCount,int demoOnly) +{ + *mapCount=0; + for(int i=0;ihideMap&&(demook||!demoOnly)) + availableMaps[(*mapCount)++]=i; + } + qsort(availableMaps,*mapCount,sizeof(tFileRef),CompareMaps); +} + +//get a list of available environment files +void GetAvailableEnvironments(int *availableEnvironments,int *environmentCount,int timeTrial) +{ + *environmentCount=0; + for(int i=0;ilastRoad=*map; +} + +void SelectPrevMap(tFileRef *map) +{ + int availableMaps[kMaxMaps]; + int mapCount; + int selection=0; + + GetAvailableMaps(availableMaps,&mapCount,!RT3_IsRegistered()); + for(int i=0;ilastRoad=*map; +} + +int SelectMapByChar(char ch) +{ + tFileRef availableMaps[kMaxMaps]; + int mapCount; + GetAvailableMaps(availableMaps,&mapCount,false); + + for(int i=0;iname)[0])>=ch) + return i; + } + return mapCount-1; +} + +typedef struct{ + float t; + int *mapSelection; + int (*Callback)(void*); + void *userData; +} tMapSelectionCallbackUserData; + + +enum{ + kMapSelectionMap, + kMapSelectionReverse, + kMapSelectionLaps, + kMapSelectionEnvironment, +// kMapSelectionMode, + kMapSelectionAccept, + kMapSelectionExit, + kNumMapSelectionItems +}; + +int MapSelectionCallback(void *ud) +{ + char ch=toupper(PeekKeyInput(NULL)); + if(ch>='A'&&ch<='Z') + { + *((tMapSelectionCallbackUserData*)ud)->mapSelection=SelectMapByChar(ch); + GetKeyInput(NULL); + return kNumMapSelectionItems; + } + if(((tMapSelectionCallbackUserData*)ud)->Callback) + return ((tMapSelectionCallbackUserData*)ud)->Callback(((tMapSelectionCallbackUserData*)ud)->userData); + return -1; +} + +int InterfaceMapSelection(tGameInfo *gInfo,int (*Callback)(void*),void *userData,int timeTrial) +{ + tFileRef availableMaps[kMaxMaps]; + int availableEnvironments[kMaxEnvironments]; + int mapSelection=0; + int environmentSelection=0; + int mapCount,environmentCount; + int lapCount=gConfig->lastLaps; + int reverse=gConfig->reverse; + + tMapSelectionCallbackUserData ud; + ud.t=INFINITY; + ud.mapSelection = &mapSelection; + ud.Callback=Callback; + ud.userData=userData; + + GetAvailableMaps(availableMaps,&mapCount,false); + for(int i=0;ilastRoad==availableMaps[i]) + mapSelection=i; + GetAvailableEnvironments(availableEnvironments,&environmentCount,timeTrial); + for(int i=0;ilastEnv==availableEnvironments[i]) + environmentSelection=i; + + tInterfaceMenuDescribtion menu; + InterfaceInitMenu(&menu,kNumMapSelectionItems,"Select Map"); + menu.RenderCallback=InterfaceRenderReplay; + InterfaceMenuZoomAnimation(&menu,-1,true); + + menu.items[kMapSelectionReverse-1].lineSpacing*=1.5; + menu.items[kMapSelectionAccept-1].lineSpacing*=4.25; + strcpy(menu.items[kMapSelectionExit].label,"Cancel"); + menu.imageXScale=kMapImageXStretch; + menu.imageYScale=kMapImageYStretch; + menu.imageXPos+=0.1; + menu.imageYPos-=0.03; + menu.TimerCallback=MapSelectionCallback; + menu.userData=&ud; + menu.returnOnSpace=true; + + for(int i=0;i<=kMapSelectionEnvironment;i++) + menu.items[i].flags|=kInterfaceMenuItemArrowInput; + + for(int i=kMapSelectionReverse;i<=kMapSelectionEnvironment;i++) + { + menu.items[i].size*=0.8; + menu.items[i].lineSpacing*=0.8; + } + + int exit=false; + int sel; + + do{ + tMapInfo *mapInfo=(tMapInfo*)FileGetParsedDataPtr(availableMaps[mapSelection],kParserTypeMapInfoDesc,sizeof(tMapInfo)); + tEnvironment *environment=(tEnvironment*)FileGetParsedDataPtr(availableEnvironments[environmentSelection],kParserTypeEnvironmentDesc,sizeof(tEnvironment)); + int demook=false; + if(availableMaps[mapSelection]==FileGetReference("city2.mapinfo"))demook=true; + if(availableMaps[mapSelection]==FileGetReference("highspeed.mapinfo"))demook=true; + menu.image=mapInfo->image; + + if(demook||RT3_IsRegistered()) + { + menu.imageAlpha=1.0; + strcpy(menu.items[kMapSelectionAccept].label,"Accept"); + menu.items[kMapSelectionAccept].flags=0; + if((!mapInfo->loop)||gInfo->numLaps==-1) + menu.items[kMapSelectionLaps].flags|=kInterfaceMenuItemDisabled; + else + menu.items[kMapSelectionLaps].flags&=~kInterfaceMenuItemDisabled; + menu.items[kMapSelectionEnvironment].flags&=~kInterfaceMenuItemDisabled; + menu.items[kMapSelectionReverse].flags&=~kInterfaceMenuItemDisabled; + } + else + { + menu.imageAlpha=0.5; + strcpy(menu.items[kMapSelectionAccept].label,"\255demo.png\255 Not available in demo!!"); + menu.items[kMapSelectionAccept].flags|=kInterfaceMenuItemDisabled; + menu.items[kMapSelectionLaps].flags|=kInterfaceMenuItemDisabled; + menu.items[kMapSelectionEnvironment].flags|=kInterfaceMenuItemDisabled; + menu.items[kMapSelectionReverse].flags|=kInterfaceMenuItemDisabled; + } + + sprintf(menu.items[kMapSelectionMap].label,"Selected Map: \255#a\255%s",mapInfo->name); + sprintf(menu.items[kMapSelectionReverse].label,"Direction: \255#a\255%s",reverse?"Reverse":"Normal"); + if(gInfo->numLaps!=-1) + if(!mapInfo->loop) + sprintf(menu.items[kMapSelectionLaps].label,"Number of Laps: \255#a\255Not Looped"); + else + sprintf(menu.items[kMapSelectionLaps].label,"Number of Laps: \255#a\255%d",lapCount); + else + sprintf(menu.items[kMapSelectionLaps].label,"Number of Laps: \255#a\255Unlimited"); + if(mapInfo->useAltEnv&&environment->hasAltEnv) + environment=(tEnvironment*)FileGetParsedDataPtr(environment->altEnv,kParserTypeEnvironmentDesc,sizeof(tEnvironment)); + sprintf(menu.items[kMapSelectionEnvironment].label,"Weather: \255#a\255%s",environment->name); + + sel=InterfaceGetUserMenuSelection(&menu); + if(sel==kNumMapSelectionItems) + menu.initialSelection=0; + else + switch(menu.initialSelection=(sel&kInterfaceMenuItemMask)) + { + case kMapSelectionMap: + if(sel&kInterfaceMenuLeftArrow) + do{ + mapSelection--; + if(mapSelection<0)mapSelection=mapCount-1; + mapInfo=(tMapInfo*)FileGetParsedDataPtr(availableMaps[mapSelection],kParserTypeMapInfoDesc,sizeof(tMapInfo)); + }while(gInfo->numLaps==-1&&!mapInfo->loop); + else if(sel&kInterfaceMenuRightArrow) + do{ + mapSelection=(mapSelection+1)%mapCount; + mapInfo=(tMapInfo*)FileGetParsedDataPtr(availableMaps[mapSelection],kParserTypeMapInfoDesc,sizeof(tMapInfo)); + }while(gInfo->numLaps==-1&&!mapInfo->loop); + else if(mapInfo->demoAvailable||RT3_IsRegistered()) + exit=true; + break; + + case kMapSelectionReverse: + if(sel&(kInterfaceMenuRightArrow|kInterfaceMenuLeftArrow)) + reverse=!reverse; + else if(mapInfo->demoAvailable||RT3_IsRegistered()) + exit=true; + break; + + case kMapSelectionLaps: + if(sel&kInterfaceMenuLeftArrow){ + lapCount--; + if(lapCount<1)lapCount=kMaxLaps; + } + else if(sel&kInterfaceMenuRightArrow){ + lapCount++; + if(lapCount>kMaxLaps)lapCount=1; + } + else if(mapInfo->demoAvailable||RT3_IsRegistered()) + exit=true; + break; + + case kMapSelectionEnvironment: + if(sel&kInterfaceMenuLeftArrow){ + environmentSelection--; + if(environmentSelection<0)environmentSelection=environmentCount-1; + } + else if(sel&kInterfaceMenuRightArrow) + environmentSelection=(environmentSelection+1)%environmentCount; + else if(mapInfo->demoAvailable||RT3_IsRegistered()) + exit=true; + break; + + /* case kMapSelectionMode: + if(sel&kInterfaceMenuLeftArrow){ + gInfo->arcade--; + if(gInfo->arcade<0) + gInfo->arcade=(sel&kInterfaceMenuEasterEgg?2:1); + } + else if(sel&kInterfaceMenuRightArrow) + gInfo->arcade=(gInfo->arcade+1)%(sel&kInterfaceMenuEasterEgg?3:2); + else exit=true; + break; + */ + case kInterfaceMenuOK: + case kMapSelectionAccept: + if(mapInfo->demoAvailable||RT3_IsRegistered()) + exit=true; + break; + + case kInterfaceMenuEsc: + case kMapSelectionExit: + exit=true; + break; + } + }while(!exit); + + InterfaceDisposeMenu(&menu); + + sel&=kInterfaceMenuItemMask; + if(sel!=kInterfaceMenuEsc&&sel!=kMapSelectionExit) + { + gInfo->reverse=reverse; + if(gInfo->numLaps!=-1) + gInfo->numLaps=lapCount; + gInfo->map=availableMaps[mapSelection]; + tMapInfo *mapInfo=(tMapInfo*)FileGetParsedDataPtr(gInfo->map,kParserTypeMapInfoDesc,sizeof(tMapInfo)); + if(!mapInfo->loop) + { + gInfo->numLaps=1; + } + gInfo->environment=availableEnvironments[environmentSelection]; + + gConfig->lastRoad=availableMaps[mapSelection]; + gConfig->reverse=reverse; + gConfig->lastLaps=lapCount; + gConfig->lastEnv=availableEnvironments[environmentSelection]; +// gConfig->arcade=gInfo->arcade; + return true; + } + return false; +} \ No newline at end of file diff --git a/source/mapselection.h b/source/mapselection.h new file mode 100644 index 0000000..2129e20 --- /dev/null +++ b/source/mapselection.h @@ -0,0 +1,14 @@ +#ifndef __MAPSELECTION +#define __MAPSELECTION + +#include "fileio.h" +#include "gameinitexit.h" + +#define kMapImageXStretch 0.35 +#define kMapImageYStretch 0.175 + +void SelectNextMap(tFileRef *map); +void SelectPrevMap(tFileRef *map); +int InterfaceMapSelection(tGameInfo *gInfo,int (*Callback)(void*),void *userData,int timeTrial); + +#endif \ No newline at end of file diff --git a/source/models.cpp b/source/models.cpp new file mode 100755 index 0000000..a14b128 --- /dev/null +++ b/source/models.cpp @@ -0,0 +1,995 @@ +//models.cpp +//3d model drawing code + +#include +#include +#include +#include +#include +#include "fileio.h" +#include "gamemem.h" +#include "vectors.h" +#include "entities.h" +#include "textures.h" +#include "renderframe.h" +#include "environment.h" +#include "transparency.h" +#include "config.h" +#include "modeltypes.h" +#include "models.h" +#include "stencil.h" +#include "network.h" + +//#define __USEDISPLAYLISTS +#ifndef __TARGET_TOOLAPP +#define __USEVERTEXARRAYS +#endif + +typedef struct{ + void *next; + tFileRef model; +} tModelList; + +tModelList *gModelList=NULL; + +#define kShadowZoom 0.99 + +//When a model is loaded it won't neccesaryly have the correct +//file reference IDs for the texture files, as file reference IDs +//are redefined each time at application startup. +//this function inserts the correct file reference IDs for a model's +//textures based on file names. +void ModelInsertTextureRefs(tFileRef modelRef) +{ + tModelData *modelData=((tModel*)gFileTable[modelRef].parsedData)->data; + tVector3 *vertices=(tVector3*)&modelData->data; + tVector3 *normals=vertices+modelData->vertexCount; + tVector2 *texels=(tVector2*)(normals+modelData->normalCount); + tFaceData *faces=(tFaceData*)(texels+modelData->texelCount); + tMaterial *materials=(tMaterial*)(faces+modelData->faceCount); + if(modelData->modelFlags&kFlagMultiTextureFlag) + materials=(tMaterial*)((tFaceDataMT*)faces+modelData->faceCount); + for(int i=0;imaterialCount;i++) + materials[i].m.texRef=FileGetReference(materials[i].texName); +} + +void StoreFaceVertices(tVector3 *vertices,tVector3 *normals,tVector2 *texels,tFaceData *faces,int i,tVertexArrayElement *store,int mt) +{ + if(mt) + if(((tFaceDataMT*)faces)[i].vertices->normal!=-1) + for(int vertex=0;vertex<3;vertex++){ + if(((tFaceDataMT*)faces)[i].vertices->texel!=-1) + store[0][vertex].texel=texels[((tFaceDataMT*)faces)[i].vertices[vertex].texel]; + else + store[0][vertex].texel=Vector(0,0); + if(((tFaceDataMT*)faces)[i].vertices->texel2!=-1) + store[0][vertex].texel2=texels[((tFaceDataMT*)faces)[i].vertices[vertex].texel2]; + else + store[0][vertex].texel2=Vector(0,0); + store[0][vertex].normal=normals[((tFaceDataMT*)faces)[i].vertices[vertex].normal]; + store[0][vertex].vertex=vertices[((tFaceDataMT*)faces)[i].vertices[vertex].vertex]; + } + else{ + //otherwise, calculate a normal. + tVector3 *a=vertices+(((tFaceDataMT*)faces)[i].vertices[0].vertex); + tVector3 *b=vertices+(((tFaceDataMT*)faces)[i].vertices[1].vertex); + tVector3 *c=vertices+(((tFaceDataMT*)faces)[i].vertices[2].vertex); + tVector3 n=!((*b-*a)%(*c-*a)); + for(int vertex=0;vertex<3;vertex++) + { + if(((tFaceDataMT*)faces)[i].vertices->texel!=-1) + store[0][vertex].texel=texels[((tFaceDataMT*)faces)[i].vertices[vertex].texel]; + else + store[0][vertex].texel=Vector(0,0); + if(((tFaceDataMT*)faces)[i].vertices->texel2!=-1) + store[0][vertex].texel2=texels[((tFaceDataMT*)faces)[i].vertices[vertex].texel2]; + else + store[0][vertex].texel2=Vector(0,0); + store[0][vertex].normal=n; + store[0][vertex].vertex=vertices[((tFaceDataMT*)faces)[i].vertices[vertex].vertex]; + } + } + else if(faces[i].vertices->normal!=-1) + for(int vertex=0;vertex<3;vertex++){ + if(faces[i].vertices->texel!=-1) + store[0][vertex].texel=texels[faces[i].vertices[vertex].texel]; + else + store[0][vertex].texel=Vector(0,0); + store[0][vertex].normal=normals[faces[i].vertices[vertex].normal]; + store[0][vertex].vertex=vertices[faces[i].vertices[vertex].vertex]; + } + else{ + //otherwise, calculate a normal. + tVector3 *a=vertices+(faces[i].vertices[0].vertex); + tVector3 *b=vertices+(faces[i].vertices[1].vertex); + tVector3 *c=vertices+(faces[i].vertices[2].vertex); + tVector3 n=!((*b-*a)%(*c-*a)); + for(int vertex=0;vertex<3;vertex++) + { + if(faces[i].vertices->texel!=-1) + store[0][vertex].texel=texels[faces[i].vertices[vertex].texel]; + else + store[0][vertex].texel=Vector(0,0); + store[0][vertex].normal=n; + store[0][vertex].vertex=vertices[faces[i].vertices[vertex].vertex]; + } + } +} + +void ModelInitArrays(tFileRef modelRef) +{ + tModelData *modelData=((tModel*)gFileTable[modelRef].parsedData)->data; + int mt=modelData->modelFlags&kFlagMultiTextureFlag; + tVector3 *vertices=(tVector3*)&modelData->data; + tVector3 *normals=vertices+modelData->vertexCount; + tVector2 *texels=(tVector2*)(normals+modelData->normalCount); + tFaceData *faces=(tFaceData*)(texels+modelData->texelCount); + tFaceDataMT *facesMT=(tFaceDataMT*)(texels+modelData->texelCount); + tMaterial *materials=(tMaterial*)(faces+modelData->faceCount); + if(mt) + materials=(tMaterial*)(facesMT+modelData->faceCount); + int matCount=mt?modelData->materialCount/2:modelData->materialCount; + + ((tModel*)gFileTable[modelRef].parsedData)->indices=(tMaterialArrayIndices*)MemoryAllocateZeroedBlock(sizeof(tMaterialArrayIndices)*matCount); + tMaterialArrayIndices *indices=((tModel*)gFileTable[modelRef].parsedData)->indices; + ((tModel*)gFileTable[modelRef].parsedData)->array=(tVertexArrayElement*)MemoryAllocateBlock(sizeof(tVertexArrayElement)*modelData->faceCount); + tVertexArrayElement *array=((tModel*)gFileTable[modelRef].parsedData)->array; + + //arrays are indexed starting with 1, so we shift all arrays + //down by one index. + vertices--; + normals--; + texels--; + materials--; + + //for each face + for(int i=0;ifaceCount;i++) + { + int mat=mt?facesMT[i].material:faces[i].material; + int faceMaterial=mat&kMaterialMask; + if(faceMaterial!=-1&&faceMaterial!=0) + if((!(materials[faceMaterial].m.flags&kMaterialUseAlphaChannel))||mat&kDisableTransparencyFlag) + indices[faceMaterial-1].num++; + else + indices[faceMaterial-1].numTransparent++; + } + + int arrayVertexCount=0; + for(int material=1;material<=matCount;material++) + { + indices[material-1].start=arrayVertexCount; + for(int i=0;ifaceCount;i++) + { + int mat=mt?facesMT[i].material:faces[i].material; + int faceMaterial=mat&kMaterialMask; + if(material==faceMaterial) + if((!(materials[faceMaterial].m.flags&kMaterialUseAlphaChannel))||mat&kDisableTransparencyFlag) + StoreFaceVertices(vertices,normals,texels,faces,i,array+arrayVertexCount++,mt); + } + for(int i=0;ifaceCount;i++) + { + int mat=mt?facesMT[i].material:faces[i].material; + int faceMaterial=mat&kMaterialMask; + if(material==faceMaterial) + if(materials[faceMaterial].m.flags&kMaterialUseAlphaChannel&&!(mat&kDisableTransparencyFlag)) + StoreFaceVertices(vertices,normals,texels,faces,i,array+arrayVertexCount++,mt); + } + } + + glGenVertexArraysAPPLE(1,&(((tModel*)gFileTable[modelRef].parsedData)->arrayRef)); + glBindVertexArrayAPPLE(((tModel*)gFileTable[modelRef].parsedData)->arrayRef); + glInterleavedArrays(GL_T2F_N3F_V3F,sizeof(tVertexArrayVertex),array); + + if(mt){ + glClientActiveTextureARB(GL_TEXTURE1_ARB); + glTexCoordPointer(2,GL_FLOAT,sizeof(tVertexArrayVertex),((char*)array)+sizeof(tVector2)+2*sizeof(tVector3)); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glClientActiveTextureARB(GL_TEXTURE0_ARB); + } +} + +enum{ + kModeDrawNormal, + kModeDrawEverythingSolid, + kModeDrawOnlyTransparent, + kModeDrawOnlyTransparentSolid, + kModeDrawOnlySolid, + kModeDrawEverythingGhosted +}; + + +void ModelAddGhostedArrays(tFileRef modelRef,int textureSet) +{ + tModelData *modelData=((tModel*)gFileTable[modelRef].parsedData)->data; + tVector3 *vertices=(tVector3*)&modelData->data; + tVector3 *normals=vertices+modelData->vertexCount; + tVector2 *texels=(tVector2*)(normals+modelData->normalCount); + tFaceData *faces=(tFaceData*)(texels+modelData->texelCount); + tMaterial *materials=(tMaterial*)(faces+modelData->faceCount); + + tMaterialArrayIndices *indices=((tModel*)gFileTable[modelRef].parsedData)->indices; + tVertexArrayElement *array=((tModel*)gFileTable[modelRef].parsedData)->array; + //arrays are indexed starting with 1, so we shift all arrays + //down by one index. + materials--; + + for(int i=0;imaterialCount;i++) + for(int j=indices[i].start;jmat=&(materials+i+1)->m; + p->ghost=true; + p->textureSet=textureSet; + for(int v=0;v<3;v++) + { + p->v[v]=array[j][v]; + p->v[v].vertex=p->v[v].vertex*gTransformDir+gTransformPos; + } + } +} + +void ModelAddTransparentArrays(tFileRef modelRef,int textureSet) +{ + tModelData *modelData=((tModel*)gFileTable[modelRef].parsedData)->data; + tVector3 *vertices=(tVector3*)&modelData->data; + tVector3 *normals=vertices+modelData->vertexCount; + tVector2 *texels=(tVector2*)(normals+modelData->normalCount); + tFaceData *faces=(tFaceData*)(texels+modelData->texelCount); + tMaterial *materials=(tMaterial*)(faces+modelData->faceCount); + + tMaterialArrayIndices *indices=((tModel*)gFileTable[modelRef].parsedData)->indices; + tVertexArrayElement *array=((tModel*)gFileTable[modelRef].parsedData)->array; + //arrays are indexed starting with 1, so we shift all arrays + //down by one index. + materials--; + + for(int i=0;imaterialCount;i++) + for(int j=indices[i].start+indices[i].num;jmat=&(materials+i+1)->m; + p->textureSet=textureSet; + for(int v=0;v<3;v++) + { + p->v[v]=array[j][v]; + p->v[v].normal=p->v[v].normal*gTransformDir; + p->v[v].vertex=p->v[v].vertex*gTransformDir+gTransformPos; + } + } +} + +void ModelDrawArrays(tFileRef modelRef,int mode,int textureSet) +{ + tModelData *modelData=((tModel*)gFileTable[modelRef].parsedData)->data; + int mt=modelData->modelFlags&kFlagMultiTextureFlag; + tVector3 *vertices=(tVector3*)&modelData->data; + tVector3 *normals=vertices+modelData->vertexCount; + tVector2 *texels=(tVector2*)(normals+modelData->normalCount); + tFaceData *faces=(tFaceData*)(texels+modelData->texelCount); + tFaceDataMT *facesMT=(tFaceDataMT*)(texels+modelData->texelCount); + tMaterial *materials=(tMaterial*)(faces+modelData->faceCount); + if(mt) + materials=(tMaterial*)(facesMT+modelData->faceCount); + int matCount=mt?modelData->materialCount/2:modelData->materialCount; + + tMaterialArrayIndices *indices=((tModel*)gFileTable[modelRef].parsedData)->indices; + + //arrays are indexed starting with 1, so we shift all arrays + //down by one index. + materials--; + + glBindVertexArrayAPPLE(((tModel*)gFileTable[modelRef].parsedData)->arrayRef); + //glInterleavedArrays(GL_T2F_N3F_V3F,0,((tModel*)gFileTable[modelRef].parsedData)->array); + + //glLockArraysEXT(0,modelData->faceCount*3); + +/* if(TexturesSelectTextureUnit(1)) + { + glTexCoordPointer(2,GL_FLOAT,sizeof(tVertexArrayVertex),((char*)((tModel*)gFileTable[modelRef].parsedData)->array)+sizeof(tVector2)+2*sizeof(tVector3)); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + TexturesSelectTextureUnit(0); + } + glTexCoordPointer(2,GL_FLOAT,sizeof(tVertexArrayVertex),((char*)((tModel*)gFileTable[modelRef].parsedData)->array)); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glNormalPointer(GL_FLOAT,sizeof(tVertexArrayVertex),((char*)((tModel*)gFileTable[modelRef].parsedData)->array)+sizeof(tVector2)); + glEnableClientState(GL_NORMAL_ARRAY); + glVertexPointer(3,GL_FLOAT,sizeof(tVertexArrayVertex),((char*)((tModel*)gFileTable[modelRef].parsedData)->array)+sizeof(tVector2)+sizeof(tVector3)); + glEnableClientState(GL_VERTEX_ARRAY);*/ + + + if(mode!=kModeDrawOnlyTransparent) + for(int i=0;im),(&(materials+i+1+matCount)->m),textureSet); + else + UseMaterial((&(materials+i+1)->m),false,textureSet); + glDrawArrays(GL_TRIANGLES,start*3,num*3); + } + //glUnlockArraysEXT(); + + if((mode==kModeDrawNormal||mode==kModeDrawOnlyTransparent)&&!mt) + ModelAddTransparentArrays(modelRef,textureSet); +} + +void ModelCompileDisplayList(tFileRef modelRef); + +void ModelCalcClipInfo(tFileRef modelRef) +{ + tVector3 minv=Vector(INFINITY,INFINITY,INFINITY); + tVector3 maxv=Vector(-INFINITY,-INFINITY,-INFINITY); + + tModelData *modelData=((tModel*)gFileTable[modelRef].parsedData)->data; + tVector3 *vertices=(tVector3*)&modelData->data; + for(int i=0;ivertexCount;i++) + { + if(vertices[i].xmaxv.x)maxv.x=vertices[i].x; + if(vertices[i].y>maxv.y)maxv.y=vertices[i].y; + if(vertices[i].z>maxv.z)maxv.z=vertices[i].z; + } + if(modelData->vertexCount) + { + ((tModel*)gFileTable[modelRef].parsedData)->center=(minv+maxv)*0.5; + ((tModel*)gFileTable[modelRef].parsedData)->centerMaxExtends=~(minv-((tModel*)gFileTable[modelRef].parsedData)->center); + } + else + { + ((tModel*)gFileTable[modelRef].parsedData)->center=Vector(0,0,0); + ((tModel*)gFileTable[modelRef].parsedData)->centerMaxExtends=0; + } +} + +void ModelSwitchEndianess(tFileRef modelRef) +{ + tModelData *modelData=((tModel*)gFileTable[modelRef].parsedData)->data; + S32Swap(modelData->vertexCount); + S32Swap(modelData->normalCount); + S32Swap(modelData->faceCount); + S32Swap(modelData->materialCount); + S32Swap(modelData->modelFlags); + S32Swap(modelData->texelCount); + + S32Swap(*((SInt32*)(&modelData->maxExtends))); + + int mt=modelData->modelFlags&kFlagMultiTextureFlag; + tVector3 *vertices=(tVector3*)&modelData->data; + tVector3 *normals=vertices+modelData->vertexCount; + tVector2 *texels=(tVector2*)(normals+modelData->normalCount); + tFaceData *faces=(tFaceData*)(texels+modelData->texelCount); + tFaceDataMT *facesMT=(tFaceDataMT*)(texels+modelData->texelCount); + tMaterial *materials=(tMaterial*)(faces+modelData->faceCount); + if(mt) + materials=(tMaterial*)(facesMT+modelData->faceCount); + + for(int i=0;ivertexCount;i++) + { + S32Swap(*((SInt32*)(&(vertices[i].x)))); + S32Swap(*((SInt32*)(&(vertices[i].y)))); + S32Swap(*((SInt32*)(&(vertices[i].z)))); + } + + for(int i=0;inormalCount;i++) + { + S32Swap(*((SInt32*)(&(normals[i].x)))); + S32Swap(*((SInt32*)(&(normals[i].y)))); + S32Swap(*((SInt32*)(&(normals[i].z)))); + } + + for(int i=0;itexelCount;i++) + { + S32Swap(*((SInt32*)(&(texels[i].x)))); + S32Swap(*((SInt32*)(&(texels[i].y)))); + } + + + if(mt) + for(int i=0;ifaceCount;i++) + { + S32Swap(facesMT[i].material); + for(int j=0;j<3;j++) + { + S32Swap(facesMT[i].vertices[j].vertex); + S32Swap(facesMT[i].vertices[j].normal); + S32Swap(facesMT[i].vertices[j].texel); + S32Swap(facesMT[i].vertices[j].texel2); + S32Swap(facesMT[i].vertices[j].neighbor); + } + } + else + for(int i=0;ifaceCount;i++) + { + S32Swap(faces[i].material); + for(int j=0;j<3;j++) + { + S32Swap(faces[i].vertices[j].vertex); + S32Swap(faces[i].vertices[j].normal); + S32Swap(faces[i].vertices[j].texel); + S32Swap(faces[i].vertices[j].neighbor); + } + } + for(int i=0;imaterialCount;i++) + { + S32Swap(materials[i].m.flags); + S32Swap(*((SInt32*)(&(materials[i].m.shininess)))); + S32Swap(*((SInt32*)(&(materials[i].m.specular.x)))); + S32Swap(*((SInt32*)(&(materials[i].m.specular.y)))); + S32Swap(*((SInt32*)(&(materials[i].m.specular.z)))); + S32Swap(*((SInt32*)(&(materials[i].m.ambient.x)))); + S32Swap(*((SInt32*)(&(materials[i].m.ambient.y)))); + S32Swap(*((SInt32*)(&(materials[i].m.ambient.z)))); + S32Swap(*((SInt32*)(&(materials[i].m.diffuse.x)))); + S32Swap(*((SInt32*)(&(materials[i].m.diffuse.y)))); + S32Swap(*((SInt32*)(&(materials[i].m.diffuse.z)))); + } +} + +//Load a model from disk +void ModelLoad(tFileRef modelRef) +{ + gFileTable[modelRef].parsedData=MemoryAllocateBlock(sizeof(tModel)); + ((tModel*)gFileTable[modelRef].parsedData)->data=(tModelData*)FileGetDataPtr(modelRef); + +#if TARGET_RT_LITTLE_ENDIAN + ModelSwitchEndianess(modelRef); +#endif + + ModelInsertTextureRefs(modelRef); +#ifdef __USEVERTEXARRAYS + ModelInitArrays(modelRef); +#endif +#ifdef __USEDISPLAYLISTS + ModelCompileDisplayList(modelRef); +#endif + gFileTable[modelRef].parsed=true; + ModelCalcClipInfo(modelRef); + + tModelList *modelList=gModelList; + gModelList=(tModelList*)MemoryAllocateBlock(sizeof(tModelList)); + gModelList->next=(void*)modelList; + gModelList->model=modelRef; + +} + +tVector3 ModelGetCenter(tFileRef modelRef) +{ + if(!gFileTable[modelRef].parsed) + ModelLoad(modelRef); + return ((tModel*)gFileTable[modelRef].parsedData)->center; +} + +float ModelGetCenterMaxExtends(tFileRef modelRef) +{ + if(!gFileTable[modelRef].parsed) + ModelLoad(modelRef); + return ((tModel*)gFileTable[modelRef].parsedData)->centerMaxExtends; +} + +//Dispose all data structures associated with a model. +void ModelDispose(tFileRef modelRef) +{ + MemoryFreeBlock(((tModel*)gFileTable[modelRef].parsedData)->data); + MemoryFreeBlock(((tModel*)gFileTable[modelRef].parsedData)->indices); + MemoryFreeBlock(((tModel*)gFileTable[modelRef].parsedData)->array); + if(gFileTable[modelRef].data!=((tModel*)gFileTable[modelRef].parsedData)->data) + MemoryFreeBlock(gFileTable[modelRef].data); + +#ifdef __USEDISPLAYLISTS + glDeleteLists(((tModel*)gFileTable[modelRef].parsedData)->ref,1); +#endif + glDeleteVertexArraysAPPLE(1,&((tModel*)gFileTable[modelRef].parsedData)->arrayRef); + MemoryFreeBlock(((tModel*)gFileTable[modelRef].parsedData)); + + gFileTable[modelRef].parsed=false; + gFileTable[modelRef].loaded=false; +} + +void ModelsUnloadAll() +{ + while(gModelList!=NULL) + { + tModelList *next=(tModelList*)gModelList->next; + ModelDispose(gModelList->model); + MemoryFreeBlock(gModelList); + gModelList=next; + } +} + +//return the maximum extends of a model. (the distance from +//the furthest vertex from center). +float ModelGetMaxExtends(tFileRef modelRef) +{ + if(!gFileTable[modelRef].parsed) + ModelLoad(modelRef); + if(!gFileTable[modelRef].parsed) + return 0; + tModelData *modelData=((tModel*)gFileTable[modelRef].parsedData)->data; + + if(!modelData->maxExtends) + { + tVector3 *vertices=(tVector3*)&modelData->data; + tVector3 maxExtends=Vector(0,0,0); + for(int i=0;ivertexCount;i++) + { + if(fabs(vertices[i].x)>maxExtends.x)maxExtends.x=fabs(vertices[i].x); + if(fabs(vertices[i].y)>maxExtends.y)maxExtends.y=fabs(vertices[i].y); + if(fabs(vertices[i].z)>maxExtends.z)maxExtends.z=fabs(vertices[i].z); + } + modelData->maxExtends=~maxExtends; + } + return modelData->maxExtends; +} + +//Draw a model +void ModelDraw(tFileRef modelRef,int mode,int textureSet) +{ + tModelData *modelData=((tModel*)gFileTable[modelRef].parsedData)->data; + tVector3 *vertices=(tVector3*)&modelData->data; + tVector3 *normals=vertices+modelData->vertexCount; + tVector2 *texels=(tVector2*)(normals+modelData->normalCount); + tFaceData *faces=(tFaceData*)(texels+modelData->texelCount); + tMaterial *materials=(tMaterial*)(faces+modelData->faceCount); + + //arrays are indexed starting with 1, so we shift all arrays + //down by one index. + vertices--; + normals--; + texels--; + materials--; + + int material=-1;//currently used material + int transparent; + + //for each face + for(int i=0;ifaceCount;i++) + { + int faceMaterial=faces[i].material&kMaterialMask; + //check if the material is the same as we were using + if(faceMaterial!=material){ + //if not, change material + transparent=UseMaterial(&((materials+faceMaterial)->m),false,textureSet); + if(mode==kModeDrawEverythingSolid) + transparent=false; + material=faceMaterial; + } + + //if the face isn't transparent, draw it + int drawTransparent=transparent&&!(faces[i].material&kDisableTransparencyFlag); + if(!drawTransparent||(drawTransparent&&mode==kModeDrawOnlyTransparentSolid)) + { + if(mode!=kModeDrawOnlyTransparent) + { + glBegin(GL_TRIANGLES); + //do we have a normal? + if(faces[i].vertices->normal!=-1) + for(int vertex=0;vertex<3;vertex++){ + if(faces[i].vertices->texel!=-1) + glTexCoord2fv(&texels[faces[i].vertices[vertex].texel].x); + glNormal3fv(&normals[faces[i].vertices[vertex].normal].x); + glVertex3fv(&vertices[faces[i].vertices[vertex].vertex].x); + } + else{ + //otherwise, calculate a normal. + tVector3 *a=vertices+(faces[i].vertices[0].vertex); + tVector3 *b=vertices+(faces[i].vertices[1].vertex); + tVector3 *c=vertices+(faces[i].vertices[2].vertex); + tVector3 n=!((*b-*a)%(*c-*a)); + glNormal3fv(&n.x); + for(int vertex=0;vertex<3;vertex++) + { + if(faces[i].vertices->texel!=-1) + glTexCoord2fv(&texels[faces[i].vertices[vertex].texel].x); + glVertex3fv(&vertices[faces[i].vertices[vertex].vertex].x); + } + } + glEnd(); + } + } + //if the face is transparent, add it to the list of transparent faces + else if(mode!=kModeDrawOnlySolid) + if(tTransparentPoly *p=GetNextTransparentPoly(false)){ + if(faces[i].vertices->normal!=-1) + for(int vertex=0;vertex<3;vertex++){ + if(faces[i].vertices->texel!=-1) + p->v[vertex].texel=texels[faces[i].vertices[vertex].texel]; + else + p->v[vertex].texel=Vector(0,0); + p->v[vertex].normal=normals[faces[i].vertices[vertex].normal]*gTransformDir; + p->v[vertex].vertex=vertices[faces[i].vertices[vertex].vertex]*gTransformDir+gTransformPos; + } + else{ + tVector3 *a=vertices+(faces[i].vertices[0].vertex); + tVector3 *b=vertices+(faces[i].vertices[1].vertex); + tVector3 *c=vertices+(faces[i].vertices[2].vertex); + tVector3 n=!((*b-*a)%(*c-*a)); + for(int vertex=0;vertex<3;vertex++) + { + if(faces[vertex].vertices->texel!=-1) + p->v[vertex].texel=texels[faces[i].vertices[vertex].texel]; + else + p->v[vertex].texel=Vector(0,0); + p->v[vertex].normal=n*gTransformDir; + p->v[vertex].vertex=vertices[faces[i].vertices[vertex].vertex]*gTransformDir+gTransformPos; + } + } + p->mat=&(materials+faces[i].material)->m; + p->textureSet=textureSet; + } + } +} + +void ModelDrawMultiTextured(tFileRef modelRef,int textureSet) +{ + tModelData *modelData=((tModel*)gFileTable[modelRef].parsedData)->data; + tVector3 *vertices=(tVector3*)&modelData->data; + tVector3 *normals=vertices+modelData->vertexCount; + tVector2 *texels=(tVector2*)(normals+modelData->normalCount); + tFaceDataMT *faces=(tFaceDataMT*)(texels+modelData->texelCount); + tMaterial *materials=(tMaterial*)(faces+modelData->faceCount); + + //arrays are indexed starting with 1, so we shift all arrays + //down by one index. + vertices--; + normals--; + texels--; + materials--; + + int material=-1;//currently used material + + //for each face + for(int i=0;ifaceCount;i++) + { + int faceMaterial=faces[i].material&kMaterialMask; + //check if the material is the same as we were using + if(faceMaterial!=material){ + //if not, change material + UseMaterial2(&((materials+faceMaterial)->m),&((materials+faceMaterial+modelData->materialCount/2)->m),textureSet); + material=faceMaterial; + } + + glBegin(GL_TRIANGLES); + //do we have a normal? + if(faces[i].vertices->normal!=-1) + for(int vertex=0;vertex<3;vertex++){ + if(faces[i].vertices->texel!=-1) + glTexCoord2fv(&texels[faces[i].vertices[vertex].texel].x); + if(faces[i].vertices->texel2!=-1) + glMultiTexCoord2fv(GL_TEXTURE1,&texels[faces[i].vertices[vertex].texel2].x); + glNormal3fv(&normals[faces[i].vertices[vertex].normal].x); + glVertex3fv(&vertices[faces[i].vertices[vertex].vertex].x); + } + else{ + //otherwise, calculate a normal. + tVector3 *a=vertices+(faces[i].vertices[0].vertex); + tVector3 *b=vertices+(faces[i].vertices[1].vertex); + tVector3 *c=vertices+(faces[i].vertices[2].vertex); + tVector3 n=!((*b-*a)%(*c-*a)); + glNormal3fv(&n.x); + for(int vertex=0;vertex<3;vertex++) + { + if(faces[i].vertices->texel!=-1) + glTexCoord2fv(&texels[faces[i].vertices[vertex].texel].x); + if(faces[i].vertices->texel2!=-1) + glMultiTexCoord2fv(GL_TEXTURE1,&texels[faces[i].vertices[vertex].texel2].x); + glVertex3fv(&vertices[faces[i].vertices[vertex].vertex].x); + } + } + glEnd(); + } +} + +void ModelLoadTextures(tFileRef modelRef) +{ + tModelData *modelData=((tModel*)gFileTable[modelRef].parsedData)->data; + tVector3 *vertices=(tVector3*)&modelData->data; + tVector3 *normals=vertices+modelData->vertexCount; + tVector2 *texels=(tVector2*)(normals+modelData->normalCount); + tFaceData *faces=(tFaceData*)(texels+modelData->texelCount); + tMaterial *materials=(tMaterial*)(faces+modelData->faceCount); + for(int i=0;imaterialCount;i++) + UseMaterial(&((materials+i)->m),false,0); +} + +void ModelCompileDisplayList(tFileRef modelRef) +{ + //create an OpenGL display list for a model + ((tModel*)gFileTable[modelRef].parsedData)->ref=glGenLists(1); + ModelLoadTextures(modelRef); + glNewList(((tModel*)gFileTable[modelRef].parsedData)->ref,GL_COMPILE); +#ifdef __USEVERTEXARRAYS + ModelDrawArrays(modelRef,kModeDrawOnlySolid,0); +#else + ModelDraw(modelRef,kModeDrawOnlySolid,0); +#endif + glEndList(); +} + +void ModelShadowPassZFail(tFileRef modelRef,tVector3 shadowVector,char *faceSide) +{ + tModelData *modelData=((tModel*)gFileTable[modelRef].parsedData)->data; + tVector3 *vertices=(tVector3*)&modelData->data; + tVector3 *normals=vertices+modelData->vertexCount; + tVector2 *texels=(tVector2*)(normals+modelData->normalCount); + vertices--; + tFaceData *faces=(tFaceData*)(texels+modelData->texelCount); + tMaterial *materials=(tMaterial*)(faces+modelData->faceCount); + materials--; + tVector3 sw=0.05*!shadowVector; + + for(int i=0;ifaceCount;i++) + if(!((materials+(faces[i].material&kMaterialMask))->m.flags&kMaterialDisableShadow)) + if(modelData->modelFlags&kFlagShadowFlag) + { + for(int n=0;n<3;n++) + if(faces[i].vertices[n].neighbor<=-1) + { + glBegin(GL_TRIANGLE_STRIP); + glVertex3fv(&(vertices[faces[i].vertices[n].vertex]*gStencilZoom-sw).x); + glVertex3fv(&(vertices[faces[i].vertices[(n+1)%3].vertex]*gStencilZoom-sw).x); + glVertex3fv(&(vertices[faces[i].vertices[n].vertex]*gStencilZoom-shadowVector).x); + glVertex3fv(&(vertices[faces[i].vertices[(n+1)%3].vertex]*gStencilZoom-shadowVector).x); + glEnd(); + #ifdef __POLYCOUNT + gPolyCount+=2; + #endif + } + + if(faceSide[i]||1) + { + glBegin(GL_TRIANGLES); + for(int j=2;j>=0;j--) + glVertex3fv(&(vertices[faces[i].vertices[j].vertex]*gStencilZoom-sw).x); + + for(int j=0;j<=2;j++) + glVertex3fv(&(vertices[faces[i].vertices[j].vertex]*gStencilZoom-shadowVector).x); + glEnd(); + } + else + { + glBegin(GL_TRIANGLES); + for(int j=2;j>=0;j--) + glVertex3fv(&(vertices[faces[i].vertices[j].vertex]*gStencilZoom-shadowVector).x); + + for(int j=0;j<=2;j++) + glVertex3fv(&(vertices[faces[i].vertices[j].vertex]*gStencilZoom-sw).x); + glEnd(); + } + } + else + if(faceSide[i]) + { + for(int n=0;n<3;n++) + if(faces[i].vertices[n].neighbor!=-1) + { + if(faceSide[faces[i].vertices[n].neighbor]!=faceSide[i]) + { + glBegin(GL_TRIANGLE_STRIP); + glVertex3fv(&(vertices[faces[i].vertices[n].vertex]*(kShadowZoom*gStencilZoom)).x); + glVertex3fv(&(vertices[faces[i].vertices[(n+1)%3].vertex]*(kShadowZoom*gStencilZoom)).x); + glVertex3fv(&(vertices[faces[i].vertices[n].vertex]*(kShadowZoom*gStencilZoom)-shadowVector).x); + glVertex3fv(&(vertices[faces[i].vertices[(n+1)%3].vertex]*(kShadowZoom*gStencilZoom)-shadowVector).x); + glEnd(); + #ifdef __POLYCOUNT + gPolyCount+=2; + #endif + } + } + + glBegin(GL_TRIANGLES); + for(int j=2;j>=0;j--) + glVertex3fv(&(vertices[faces[i].vertices[j].vertex]*(kShadowZoom*gStencilZoom)).x); + glEnd(); + } + else + { + glBegin(GL_TRIANGLES); + for(int j=2;j>=0;j--) + glVertex3fv(&(vertices[faces[i].vertices[j].vertex]*(kShadowZoom*gStencilZoom)-shadowVector).x); + glEnd(); + } + #ifdef __POLYCOUNT + gPolyCount+=modelData->faceCount; + #endif +} + + +void ModelShadowPassZPass(tFileRef modelRef,tVector3 shadowVector,char *faceSide) +{ + tModelData *modelData=((tModel*)gFileTable[modelRef].parsedData)->data; + tVector3 *vertices=(tVector3*)&modelData->data; + tVector3 *normals=vertices+modelData->vertexCount; + tVector2 *texels=(tVector2*)(normals+modelData->normalCount); + vertices--; + tFaceData *faces=(tFaceData*)(texels+modelData->texelCount); + tMaterial *materials=(tMaterial*)(faces+modelData->faceCount); + materials--; + + if(modelData->modelFlags&kFlagShadowFlag) + { + tVector3 sw=0.05*!shadowVector; + for(int i=0;ifaceCount;i++) + if(!((materials+(faces[i].material&kMaterialMask))->m.flags&kMaterialDisableShadow)) + { + for(int n=0;n<3;n++) + if(faces[i].vertices[n].neighbor==-1) + { + glBegin(GL_TRIANGLE_STRIP); + glVertex3fv(&(vertices[faces[i].vertices[n].vertex]*gStencilZoom-sw).x); + glVertex3fv(&(vertices[faces[i].vertices[(n+1)%3].vertex]*gStencilZoom-sw).x); + glVertex3fv(&(vertices[faces[i].vertices[n].vertex]*gStencilZoom-shadowVector).x); + glVertex3fv(&(vertices[faces[i].vertices[(n+1)%3].vertex]*gStencilZoom-shadowVector).x); + glEnd(); + #ifdef __POLYCOUNT + gPolyCount+=2; + #endif + } + } + } + else for(int i=0;ifaceCount;i++) + if(!((materials+(faces[i].material&kMaterialMask))->m.flags&kMaterialDisableShadow)) + if(faceSide[i]||(modelData->modelFlags&kFlagShadowFlag)) + for(int n=0;n<3;n++) + if(faces[i].vertices[n].neighbor!=-1) + if(faceSide[faces[i].vertices[n].neighbor]!=faceSide[i]) + { + glBegin(GL_TRIANGLE_STRIP); + glVertex3fv(&(vertices[faces[i].vertices[n].vertex]*(kShadowZoom*gStencilZoom)).x); + glVertex3fv(&(vertices[faces[i].vertices[(n+1)%3].vertex]*(kShadowZoom*gStencilZoom)).x); + glVertex3fv(&(vertices[faces[i].vertices[n].vertex]*(kShadowZoom*gStencilZoom)-shadowVector).x); + glVertex3fv(&(vertices[faces[i].vertices[(n+1)%3].vertex]*(kShadowZoom*gStencilZoom)-shadowVector).x); + glEnd(); + #ifdef __POLYCOUNT + gPolyCount+=2; + #endif + } +} + +//for each face of a model, calculate whether it faces the light or not. +//return the results in the array faceSide +void CalcFaceSides(tFileRef modelRef,tVector3 lightDir,char **faceSide) +{ + tModelData *modelData=((tModel*)gFileTable[modelRef].parsedData)->data; + tVector3 *vertices=(tVector3*)&modelData->data; + tVector3 *normals=vertices+modelData->vertexCount; + tVector2 *texels=(tVector2*)(normals+modelData->normalCount); + vertices--; + tFaceData *faces=(tFaceData*)(texels+modelData->texelCount); + *faceSide=(char*)MemoryAllocateBlock(sizeof(char)*modelData->faceCount+10); + + for(int i=0;ifaceCount;i++){ + tVector3 *a=vertices+(faces[i].vertices[0].vertex); + tVector3 *b=vertices+(faces[i].vertices[1].vertex); + tVector3 *c=vertices+(faces[i].vertices[2].vertex); + + //calculate a normal to the face + tVector3 n=((*b-*a)%(*c-*a)); + + //use dot product to determine if the face faces the light + (*faceSide)[i]=n*lightDir>0; + } +} + +//Draw the shadow of a model +//Uses s stencil buffer-based shadow volume algorithm +void ModelCastShadow(tFileRef modelRef,float shadowLength) +{ + tVector3 lightDir=gEnvironment->lightDir; + if(gMapEnv) + if(!VectorZero(gMapEnv->lightDir)) + lightDir=gMapEnv->lightDir; + + tMatrix3 tr; + MatrixTranspose(gTransformDir,tr); + lightDir=lightDir*tr; + + char *faceSide; + CalcFaceSides(modelRef,lightDir,&faceSide); + + //disable actual drawing + glDisable(GL_LIGHTING); + glDisable(GL_FOG); + glDepthMask(GL_FALSE); + glColorMask(0, 0, 0, 0); + + //enable stencil buffer drawing + glEnable(GL_STENCIL_TEST); + glStencilFunc(GL_ALWAYS, 0, 0xffffffff); + + tVector3 lightNormal1=!((gTransformPos-gCameraEntity->pos)%lightDir); + tVector3 lightNormal2=lightNormal1%lightDir; + tVector3 shadowVector=lightDir*shadowLength; + //is the camera possibly in the shadow volume? + if(-lightNormal2*(gTransformPos-gCameraEntity->pos)ref); +#ifdef __USEVERTEXARRAYS + ModelDrawArrays(modelRef,allowTransparency?kModeDrawOnlyTransparent:kModeDrawOnlyTransparentSolid,textureSet); +#else + ModelDraw(modelRef,allowTransparency?kModeDrawOnlyTransparent:kModeDrawOnlyTransparentSolid,textureSet); +#endif +#else +#ifdef __USEVERTEXARRAYS + if(allowTransparency==kTransparencyGhost) + ModelAddGhostedArrays(modelRef,textureSet); + else + ModelDrawArrays(modelRef,allowTransparency?kModeDrawNormal:kModeDrawEverythingSolid,textureSet); +#else + if((((tModel*)gFileTable[modelRef].parsedData)->data)->modelFlags&kFlagMultiTextureFlag) + ModelDrawMultiTextured(modelRef,textureSet); + else + ModelDraw(modelRef,allowTransparency?kModeDrawNormal:kModeDrawEverythingSolid,textureSet); +#endif +#endif +#ifdef __POLYCOUNT + gPolyCount+=((tModelData*)((tModel*)gFileTable[modelRef].parsedData)->data)->faceCount; +#endif + if(TexturesSelectTextureUnit(1)){ + glDisable(GL_TEXTURE_2D); + glDisable(GL_TEXTURE_3D); + glDisable(GL_TEXTURE_GEN_S); + glDisable(GL_TEXTURE_GEN_T); + TexturesSelectTextureUnit(0); + } +} + +//draw a model's shadow +void DrawModelShadow(tFileRef modelRef,float shadowLength) +{ + if(!gFileTable[modelRef].parsed) + ModelLoad(modelRef); + if(!gFileTable[modelRef].parsed) + return; + if((((tModel*)gFileTable[modelRef].parsedData)->data)->modelFlags&kFlagMultiTextureFlag) + return; + if(shadowLength>0) + ModelCastShadow(modelRef,shadowLength); +} + diff --git a/source/models.h b/source/models.h new file mode 100755 index 0000000..3e0bfd3 --- /dev/null +++ b/source/models.h @@ -0,0 +1 @@ +#ifndef __MODELS #define __MODELS #include "fileio.h" #include "vectors.h" enum{ kTransparencyOff, kTransparencyOn, kTransparencyGhost }; extern float gShadowZoom; void ModelLoad(tFileRef modelRef); void ModelDispose(tFileRef modelRef); void ModelsUnloadAll(); void DrawModel(tFileRef modelRef,int allowTransparency,int textureSet); void DrawModelShadow(tFileRef modelRef,float shadowLength); float ModelGetMaxExtends(tFileRef modelRef); tVector3 ModelGetCenter(tFileRef modelRef); float ModelGetCenterMaxExtends(tFileRef modelRef); #endif \ No newline at end of file diff --git a/source/modeltypes.h b/source/modeltypes.h new file mode 100644 index 0000000..5ba4bdf --- /dev/null +++ b/source/modeltypes.h @@ -0,0 +1,78 @@ +//modeltypes.h +//type definitions for 3d models + +#ifndef __MODELTYPES +#define __MODELTYPES + +#include "transparency.h" +#include + +#define kDisableTransparencyFlag 0x80000000 +#define kMaterialMask (~kDisableTransparencyFlag) +#define kFlagShadowFlag 1<<0 +#define kFlagMultiTextureFlag 1<<1 + +//one vertex of a model's face. +typedef struct{ + int vertex; //the index of the actual vertex coordinates + int normal; //the index of the vertex normal (or -1 for none) + int texel; //the index of the texture coordinates + int neighbor;//the index of the face sharing the edge belonging to this vertex (or -1 for none) +} tFaceVertex; + +//one face of a model +typedef struct{ + int material; + tFaceVertex vertices[3]; +} tFaceData; + +//one vertex of a model's face. +typedef struct{ + int vertex; //the index of the actual vertex coordinates + int normal; //the index of the vertex normal (or -1 for none) + int texel,texel2; //the index of the texture coordinates + int neighbor;//the index of the face sharing the edge belonging to this vertex (or -1 for none) +} tFaceVertexMT; + +//one face of a model +typedef struct{ + int material; + tFaceVertexMT vertices[3]; +} tFaceDataMT; + +//the model data structure +typedef struct{ + int vertexCount,normalCount,texelCount,faceCount,materialCount;//number of vertices,normal, etc... used + int modelFlags; //model flags + float maxExtends; //the distance between the farest vertex and the point (0,0,0) + char data[1]; //the actual model data: + // -vertexCount vertices as tVector3 + // -normalCount normals as tVector3 + // -texelCount texels as tVector2 + // -faceCount faces as tFaceData + // -materialCount materials as tMaterial +} tModelData; + +//a model's material structure +typedef struct{ + char texName[32];//the filename of the texture used. + tPolyMaterial m;//and the material structure +}tMaterial; + + +typedef struct{ + int start,num,numTransparent; +} tMaterialArrayIndices; + +//a pointer to the model data and a GL display list reference +typedef struct{ + GLuint arrayRef,ref; + tMaterialArrayIndices *indices; + tVertexArrayElement *array; + tModelData *data; + tVector3 center; + float centerMaxExtends; +}tModel; + + +#endif \ No newline at end of file diff --git a/source/music.cpp b/source/music.cpp new file mode 100644 index 0000000..5db12e0 --- /dev/null +++ b/source/music.cpp @@ -0,0 +1,69 @@ +//#include "RDriver.h" // Mad Driver functions & globals +#include "fileio.h" +#include "error.h" +#include "config.h" +#include "music.h" +/* +MADDriverRec *gMADDriver; +MADLibrary *gMADLib; +MADMusic *gMADMusic; +*/ +int gMusicPlaying=false; +tFileRef gSongRef=-1; + +void MyDebugStr(short s, Ptr p1, Ptr p2) +{ + FailWithErrorString(p2); +} + +void MusicInit() +{ +/* MADDriverSettings init; + MADGetBestDriver(&init); + HandleError(MADInitLibrary(nil,init.sysMemory,&gMADLib)); + HandleError(MADCreateDriver(&init,gMADLib,&gMADDriver)); + MADStartDriver(gMADDriver); + MusicSetVolume();*/ +} + +void MusicStopSong() +{ +/* if(gMusicPlaying) + { + gMusicPlaying=false; + MADStopMusic(gMADDriver); // Stop reading current partition + MADCleanDriver(gMADDriver); + MADDisposeMusic(&gMADMusic,gMADDriver); // Dispose the current music + }*/ +} + +void MusicPlaySong(tFileRef song) +{ +/* gSongRef=song; + if(gConfig->musicVolume!=0) + { + if(gMusicPlaying) + MusicStopSong(); + HandleError(MADLoadMusicPtr(&gMADMusic,(char*)FileGetDataPtr(song))); + MADAttachDriverToMusic(gMADDriver,gMADMusic,0L); + MADPlayMusic(gMADDriver); + gMusicPlaying=true; + MusicSetVolume(); + }*/ +} + +void MusicSetVolume() +{ + /*gMADDriver->VolGlobal=64*gConfig->musicVolume; + if(gConfig->musicVolume==0) + MusicStopSong(); + else if(!gMusicPlaying&&gSongRef!=-1) + MusicPlaySong(gSongRef);*/ +} + +void MusicExit() +{ +/* MADStopDriver(gMADDriver); // Stop driver interrupt function + MADDisposeDriver(gMADDriver); // Dispose music driver + MADDisposeLibrary(gMADLib); // Close music library*/ +} \ No newline at end of file diff --git a/source/music.h b/source/music.h new file mode 100644 index 0000000..125939d --- /dev/null +++ b/source/music.h @@ -0,0 +1,10 @@ +#ifndef __MUSIC +#define __MUSIC + +void MusicInit(); +void MusicPlaySong(tFileRef song); +void MusicStopSong(); +void MusicExit(); +void MusicSetVolume(); + +#endif \ No newline at end of file diff --git a/source/network.h b/source/network.h new file mode 100644 index 0000000..ff10696 --- /dev/null +++ b/source/network.h @@ -0,0 +1,126 @@ +#ifndef __NETWORK +#define __NETWORK + +#include "gameinitexit.h" + +enum{ + kMessageTypeSynch=1, + kMessageTypeSynchStart=2, + kMessageTypeID=3, + kMessageTypeCarID=4, + kMessageTypeStart=5, + kMessageTypeEndGame=6, + kMessageTypeEndReplay=7, + kMessageTypeGameInfo=8, + kMessageTypePhysics=9, + kMessageTypeChat=10, + kMessageTypePing=11, + kMessageTypePong=12, + kMessageTypeJoinDenied=13, + kMessageTypeVersionConfirmed=14, + kMessageTypeVersionDenied=15, + kMessageTypePause=16, + kMessageTypeResume=17, + kMessageTypePlayerLeft=18, + kMessageTypePlayerJoined=19, + kMessageTypeGameTerminated=20, + kMessageTypeVersion=21, + kMessageTypeKicked=22, + kMessageTypeBye=23, + kMessageTypeFinishTime=24, +}; + +enum{ + kJoinDeniedInProgress, + kJoinDeniedVersion +}; + +enum{ + kMessagePriorityLow, + kMessagePriorityNormal, + kMessagePriorityHigh +}; + +enum{ + kMessageNoRecipients=-1, + kMessageSendToAll=-2, + kMessageSendToAllButSelf=-3, + kMessageSendToHostOnly=-4 +}; + +typedef struct{ + UInt8 playerID; + UInt8 color; + UInt64 license; + UInt32 licenseCopies; + char carName[kMaxFileNameLength]; + char playerName[256]; +// UInt32 checksum; +}tCarIDMessage; + +enum{ + kChatFlagAlert=1<<0, + kChatFlagSystem=1<<1, + kChatFlagSilent=1<<2 +}; + +typedef struct{ + UInt8 flags; + float recvTime; + char str[256]; +} tChatMessage; + +typedef struct{ + int player; + int time; +}tFinishTimeMessage; + +typedef struct{ + void *opaqueRef; + void *data; + long size; + int from; + char what; +} tNetworkPacket; + +enum{ + kNetworkDisconnectBan, + kNetworkDisconnectLicenseCopies, + kNetworkDisconnectPirate, + kNetworkDisconnectNoDemo, +}; + +void NetworkInit(); +void NetworkExit(); +int JoinNetGame(char *address,char *errorString); +void HostNetGame(int maxPlayers,char *password); +int NetworkSynch(int numPlayers); +void ExitNetGame(); +int NetworkGetLocalNetID(); +void NetworkSendPacket(char messageType,void *message,long size,int priority,int to); +void NetworkSendPacket(char messageType,void *message,long size,int priority,int to,int notTo); +void NetworkDisconnectPlayer(int netID,UInt8 type); +void NetworkIdle(); +int NetworkReceivePacket(tNetworkPacket* packet); +void NetworkDisposePacket(tNetworkPacket* packet); +void NetworkQueuePacket(tNetworkPacket *insert); +void NetworkClearPacketQueue(); +void NetworkGetStatusString(char *str); +void NetworkGetBandwidth(float *rcv,float *snd); +void NetworkPreSession(); +float NetworkGetPlayerPing(int netID); +void NetworkLockOutNewPlayers(); +void NetworkUnlockOutNewPlayers(); +void NetworkChangePassword(char *pass); + +#define S64Swap(value) ((value)=EndianS64_BtoN(value)) +#define U64Swap(value) ((value)=EndianU64_BtoN(value)) +#define S32Swap(value) ((value)=EndianS32_BtoN(value)) +#define U32Swap(value) ((value)=EndianU32_BtoN(value)) +#define S16Swap(value) ((value)=EndianS16_BtoN(value)) +#define U16Swap(value) ((value)=EndianU16_BtoN(value)) +#define F32Swap(value) ((*((UInt32*)(&(value))))=EndianU32_BtoN(*((UInt32*)(&(value))))) +#define F64Swap(value) ((*((UInt64*)(&(value))))=EndianU64_BtoN(*((UInt64*)(&(value))))) + +extern int gInternetAvailable; +#endif \ No newline at end of file diff --git a/source/network_NT.cpp b/source/network_NT.cpp new file mode 100644 index 0000000..dc1b4b3 --- /dev/null +++ b/source/network_NT.cpp @@ -0,0 +1,924 @@ +//network.cpp +//basic game hosting/joining and message sending code based on Network_Tool + +#include +#include +#include +#include "gametime.h" +#include "config.h" +#include "network.h" +#include "error.h" +#include "gameframe.h" +#include "controls.h" +#include "gamesound.h" +#include "carphysics.h" +#include "interfaceutil.h" +#include "gamemem.h" +#include "Reggie.h" +#include "gamesystem.h" +#include "interfacemultiplayer.h" +#include "initexit.h" +#include "reg_tool_3.h" + +extern ReggieRef gReggieRef; +extern int gReggieConnected; +int gAllowCompression=true; +#define kGameName "Redline" +#define kConnectionTimeOut 5.0 +#define kCompressionSize 32 +#define kCompressionFlag 0x80 + +NT_SessionRef gSessionRef=nil; + +typedef struct{ + QStub qstub; + tNetworkPacket packet; +}tQueuedPacket; + +Queue gPacketQueue; +int gInternetAvailable=true; +int gTerminateSession=false; +float gLastPingReceived=0,gLastRTT=0; +char gHistoryBuffer[1024*500]=""; +int gHistoryDumps=0; + +void NetworkLockOutNewPlayers() +{ +// HandleError(NT_SetSessionLock(gSessionRef,true)); + int err=NT_SetSessionLock(gSessionRef,true); + //PrintConsoleString("Locking session. returns %d",err); +} + +void NetworkUnlockOutNewPlayers() +{ +// HandleError(NT_SetSessionLock(gSessionRef,false)); + int err=NT_SetSessionLock(gSessionRef,false); + //PrintConsoleString("Unlocking session. returns %d",err); +} + +int NetworkGetLocalNetID() +{ + NT_MemberInfo info; + NT_GetSelfMemberInfo(gSessionRef,&info); + return info.member; +} + +void NetworkClearPacketQueue() +{ + int temp=gTerminateSession; + gTerminateSession=false; + tNetworkPacket packet; + while(NetworkReceivePacket(&packet)) + if(packet.data) + MemoryFreeBlock(packet.data); + gTerminateSession=temp; +} + +Result32 CompressPacket(void *buffer, long *length) { + Result32 error = eCommonErrorNone; + void ** indirect = NULL; + + qThrowIfNull(indirect = IndirectInitialize(buffer, *length), + eCommonErrorOutOfMem, kCommonErrorOutOfMemStr); + + qThrowIfError(IndirectCompress(indirect, + kIndirectCompressorZLib, NULL), 0); + + MemoryCopy(buffer, *indirect, *length = IndirectGetSize(indirect)); + +CLEANUP: + if (indirect) IndirectDeallocate(indirect); + return(error); +} + +Result32 DecompressPacket(void *buffer, long *length) { + Result32 error = eCommonErrorNone; + void ** indirect = NULL; + + qThrowIfNull(indirect = IndirectInitialize(buffer, *length), + eCommonErrorOutOfMem, kCommonErrorOutOfMemStr); + + qThrowIfError(IndirectDecompress(indirect, + kIndirectCompressorZLib, NULL), 0); + + MemoryCopy(buffer, *indirect, *length = IndirectGetSize(indirect)); + +CLEANUP: + if (indirect) IndirectDeallocate(indirect); + return(error); +} + +void NetworkGetStatusString(char *str) +{ + NT_ProblemInfo prob; + if(NT_GetProblemInfo(gSessionRef,0,&prob)) + { + if(prob.timeout<10) + str[0]='.'; + else if(prob.timeout<50) + str[0]='t'; + else + str[0]='T'; + if(prob.latency<10) + str[1]='.'; + else if(prob.latency<50) + str[1]='l'; + else + str[1]='L'; + if(prob.stalled<10) + str[2]='.'; + else if(prob.stalled<50) + str[2]='s'; + else + str[2]='S'; + if(prob.backlog<10) + str[3]='.'; + else if(prob.backlog<50) + str[3]='b'; + else + str[3]='B'; + if(prob.failure<10) + str[4]='.'; + else if(prob.failure<50) + str[4]='f'; + else + str[4]='F'; + if(prob.errors<10) + str[5]='.'; + else if(prob.errors<50) + str[5]='e'; + else + str[5]='E'; + str[6]='\0'; + } +} + +void NetworkGetBandwidth(float *rcv,float *snd) +{ + NT_LatencyInfo lat; + NT_GetTotalLatencyInfo(gSessionRef,&lat); + *rcv=lat.recvPackets10Sec*0.1; + *snd=lat.sendPackets10Sec*0.1; +} + +void NetworkQueuePacket(tNetworkPacket *insert) +{ + tQueuedPacket *q=(tQueuedPacket*)MemoryAllocateZeroedBlock(sizeof(tQueuedPacket)); + q->packet=*insert; + QueueInsert(&gPacketQueue,(QStubPtr)q); +} + +//#define PACKET_DUMP + +#ifdef PACKET_DUMP +void DumpPacket(tNetworkPacket *packet) +{ + char st[256]; + sprintf(st,"what: %d",packet->what); + PrintConsoleString(st); + sprintf(st,"from: %d",packet->from); + PrintConsoleString(st); + sprintf(st,"size: 0x%x",packet->size); + PrintConsoleString(st); + + int line=0; + + while(line<=packet->size/16) + { + char dataStr[256]; + + sprintf(dataStr,"%04x ",line*16); + + for(int pos=line*16;possize) + sprintf(dataStr,"%s%02x",dataStr,*((UInt8*)packet->data+pos)); + else + sprintf(dataStr,"%s ",dataStr); + + sprintf(dataStr,"%s ",dataStr); + + for(int pos=line*16;possize) + if(*((UInt8*)packet->data+pos)>=32) + sprintf(dataStr,"%s%c",dataStr,*((UInt8*)packet->data+pos)); + else + sprintf(dataStr,"%s.",dataStr); + else + sprintf(dataStr,"%s ",dataStr); + + PrintConsoleString(dataStr); + line++; + } + + PrintConsoleString(""); +} +#endif + +void NTEventHandler(NT_SessionRef sessionRef,void *refcon,NT_SessionEventType event,NT_MemberIDType member) +{ + gAllowCompression=false; + switch(event) + { + case eSessionEventSessionStart:{ + //PrintConsoleString("eSessionEventSessionStart"); + tNetworkPacket packet; + packet.what=kMessageTypePlayerJoined; + packet.from=0; + packet.size=0; + packet.data=NULL; + NetworkQueuePacket(&packet); + } + break; + case eSessionEventMemberJoin:{ + //PrintConsoleString("eSessionEventMemberJoin, member=%d",member); + tNetworkPacket packet; + packet.what=kMessageTypePlayerJoined; + packet.from=member; + packet.size=0; + packet.data=NULL; + NetworkQueuePacket(&packet); + /*NT_ToleranceInfo tolerance; + NT_GetToleranceInfo(gSessionRef,&tolerance); + PrintConsoleString("Tolerance info:"); + PrintConsoleString("avgPackets %d.",tolerance.avgPackets); + PrintConsoleString("avgRequests %d.",tolerance.avgRequests); + PrintConsoleString("maxRequests %d.",tolerance.maxRequests); + PrintConsoleString("minHeartbeat %d.",tolerance.minHeartbeat); + PrintConsoleString("avgHeartbeat %d.",tolerance.avgHeartbeat); + PrintConsoleString("maxHeartbeat %d.",tolerance.maxHeartbeat); + PrintConsoleString("maxTimeout %d.",tolerance.maxTimeout); + PrintConsoleString("maxFailures %d.",tolerance.maxFailures); + PrintConsoleString("maxErrors %d.",tolerance.maxErrors); + PrintConsoleString("minLinger %d.",tolerance.minLinger);*/ + } + break; + case eSessionEventMemberLeave:{ + //PrintConsoleString("eSessionEventMemberLeave, member=%d",member); + tNetworkPacket packet; + if(member==0) + { + if(*gDisconnectString=='\0') + sprintf(gDisconnectString,"Network Failure (Failure on Host side)."); + packet.what=kMessageTypeGameTerminated; + } + else + packet.what=kMessageTypePlayerLeft; + packet.from=member; + packet.size=0; + packet.data=NULL; + NetworkQueuePacket(&packet); + NT_LatencyInfo latency; + if(NT_GetLatencyInfo(gSessionRef,member,&latency)) + { + PrintConsoleString("Latency info for dropped member:"); + PrintConsoleString("minLinkRTT %d.",latency.minLinkRTT); + PrintConsoleString("avgLinkRTT %d.",latency.avgLinkRTT); + PrintConsoleString("p80LinkRTT %d.",latency.p80LinkRTT); + PrintConsoleString("devLinkRTT %d.",latency.devLinkRTT); + PrintConsoleString("wAvgLinkRTT %d.",latency.wAvgLinkRTT); + PrintConsoleString("wDevLinkRTT %d.",latency.wDevLinkRTT); + PrintConsoleString("minTranRTT %d.",latency.minTranRTT); + PrintConsoleString("avgTranRTT %d.",latency.avgTranRTT); + PrintConsoleString("p80TranRTT %d.",latency.p80TranRTT); + PrintConsoleString("maxTranRTT %d.",latency.maxTranRTT); + PrintConsoleString("devTranRTT %d.",latency.devTranRTT); + PrintConsoleString("wAvgTranRTT %d.",latency.wAvgTranRTT); + PrintConsoleString("wDevTranRTT %d.",latency.wDevTranRTT); + } + NT_ProblemInfo prob; + if(NT_GetProblemInfo(gSessionRef,member,&prob)) + { + PrintConsoleString("Problem info for dropped member:"); + PrintConsoleString("timeout %d.",prob.timeout); + PrintConsoleString("latency %d.",prob.latency); + PrintConsoleString("stalled %d.",prob.stalled); + PrintConsoleString("backlog %d.",prob.backlog); + PrintConsoleString("failure %d.",prob.failure); + PrintConsoleString("errors %d.",prob.errors); + } + + NT_PacketHistory(gSessionRef,gHistoryBuffer,1024*500); + } + break; + case eSessionEventSessionEnd:{ + //PrintConsoleString("eSessionEventSessionEnd"); + /* tNetworkPacket packet; + packet.what=kMessageTypeGameTerminated; + packet.from=member; + packet.size=0; + packet.data=NULL; + NetworkQueuePacket(&packet);*/ + } + break; + case eSessionEventRecvPacket:{ + UInt8 data[kNetworkToolPacketSize]; + UInt16 len; + if(NT_RecvPacket(gSessionRef,&member,(char*)data,kNetworkToolPacketSize,&len)) + { + tNetworkPacket packet; + packet.data=MemoryAllocateBlock(kNetworkToolPacketSize); + MemoryMove(packet.data,data+1,len-1); + packet.size=len-1; + packet.from=member; + packet.what=*data; + if(packet.what==kMessageTypeGameTerminated) + gTerminateSession=true; + /*#ifdef PACKET_DUMP + PrintConsoleString("Packet Received:"); + DumpPacket(&packet); + #endif + */ + //got a Ping request? + if(packet.what==kMessageTypePing) + //send out a reply packet with the original ping packets time stamp. + NetworkSendPacket(kMessageTypePong,packet.data,sizeof(float),kMessagePriorityLow,packet.from); + else if(packet.what==kMessageTypePong) + { + gLastPingReceived=TimeGetSeconds(); + gLastRTT=gLastPingReceived-*(float*)packet.data; + } + else + NetworkQueuePacket(&packet); + } + } + break; + case eSessionEventRecvRequest:{ + UInt8 data[kNetworkToolPacketSize]; + UInt16 len; + if(NT_RecvRequest(gSessionRef,&member,(char*)data,kNetworkToolPacketSize,&len)) + { + NT_SendResponse(gSessionRef,NULL,0); + tNetworkPacket packet; + packet.data=MemoryAllocateBlock(kNetworkToolPacketSize); + MemoryMove(packet.data,data+1,len-1); + packet.size=len-1; + packet.from=member; + packet.what=*data; + if(packet.what==kMessageTypeGameTerminated) + gTerminateSession=true; + + #ifdef PACKET_DUMP + PrintConsoleString("Request Packet Received:"); + DumpPacket(&packet); + #endif + + NetworkQueuePacket(&packet); + } + } + break; + case eSessionEventRecvResponse:{ + UInt8 data[kNetworkToolPacketSize]; + UInt16 len; + NT_RecvResponse(gSessionRef,&member,(char*)data,kNetworkToolPacketSize,&len); + } + break; + } + gAllowCompression=true; +} + +void NTErrorHandler(NT_SessionRef sessionRef,void *refcon,NT_SessionProblemType problem,NT_SessionSeverityType severity,NT_MemberIDType member,Bool8 *disconnectSelf,Bool8 *disconnectMember) +{ + gAllowCompression=false; + if(severity>=100) + { + PrintConsoleString("NT Error %d",problem); + + NT_MemberInfo info; + NT_GetSelfMemberInfo(gSessionRef,&info); + if(info.member==0) + { + if(member!=0) + { + *disconnectMember=true; + + tChatMessage m; + m.flags=kChatFlagSystem; + switch(problem) + { + case eSessionProblemTimeout: + sprintf(m.str,"### Player %d is disconnecting (Connection timed out).",member+1); + break; + case eSessionProblemLatency: + sprintf(m.str,"### Player %d is disconnecting (Latency too high).",member+1); + break; + case eSessionProblemStalled: + sprintf(m.str,"### Player %d is disconnecting (Stalled).",member+1); + break; + case eSessionProblemBacklog: + sprintf(m.str,"### Player %d is disconnecting (Backlog).",member+1); + break; + case eSessionProblemFailure: + sprintf(m.str,"### Player %d is disconnecting (Failure).",member+1); + break; + case eSessionProblemErrors: + sprintf(m.str,"### Player %d is disconnecting (Too many errors).",member+1); + break; + default: + sprintf(m.str,"### Player %d is disconnecting (Unknown error).",member+1); + break; + } + printf("%s\n",m.str); + //NetworkSendPacket(kMessageTypeChat,&m,sizeof(m),kMessagePriorityHigh,kMessageSendToAll); + } + } + else if(member==0) + { + *disconnectMember=false; + *disconnectSelf=true; + tNetworkPacket packet; + packet.data=NULL; + packet.size=0; + packet.from=member; + packet.what=kMessageTypeGameTerminated; + NetworkQueuePacket(&packet); + switch(problem) + { + case eSessionProblemTimeout: + sprintf(gDisconnectString,"Network Failure (Connection timed out)."); + break; + case eSessionProblemLatency: + sprintf(gDisconnectString,"Network Failure (Latency too high)."); + break; + case eSessionProblemStalled: + sprintf(gDisconnectString,"Network Failure (Stalled)."); + break; + case eSessionProblemBacklog: + sprintf(gDisconnectString,"Network Failure (Backlog)."); + break; + case eSessionProblemFailure: + sprintf(gDisconnectString,"Network Failure (Failure)."); + break; + case eSessionProblemErrors: + sprintf(gDisconnectString,"Network Failure (Too many errors)."); + break; + default: + sprintf(gDisconnectString,"Network Failure (unknown error)."); + break; + } + } + } + gAllowCompression=true; +} + +void NetworkPreSession() +{ + if(gSessionRef==NULL) + HandleError(NT_SessionStartParam(&gSessionRef,NTEventHandler,NTErrorHandler,NULL,NULL,6,kGameName,"")); + else + HandleError(NT_SessionRestart(gSessionRef,6,kGameName,"")); +} + +float NetworkGetPlayerPing(int netID) +{ + NT_MemberInfo info; + NT_GetSelfMemberInfo(gSessionRef,&info); + if(netID==info.member||netID==kMessageNoRecipients) + return 0; + + NT_LatencyInfo latency; + if(NT_GetLatencyInfo(gSessionRef,netID,&latency)); + { + /* PrintConsoleString("Player %d latency.",netID); + PrintConsoleString("minLinkRTT %d.",latency.minLinkRTT); + PrintConsoleString("avgLinkRTT %d.",latency.avgLinkRTT); + PrintConsoleString("p80LinkRTT %d.",latency.p80LinkRTT); + PrintConsoleString("devLinkRTT %d.",latency.devLinkRTT); + PrintConsoleString("wAvgLinkRTT %d.",latency.wAvgLinkRTT); + PrintConsoleString("wDevLinkRTT %d.",latency.wDevLinkRTT); + PrintConsoleString("minTranRTT %d.",latency.minTranRTT); + PrintConsoleString("avgTranRTT %d.",latency.avgTranRTT); + PrintConsoleString("p80TranRTT %d.",latency.p80TranRTT); + PrintConsoleString("maxTranRTT %d.",latency.maxTranRTT); + PrintConsoleString("devTranRTT %d.",latency.devTranRTT); + PrintConsoleString("wAvgTranRTT %d.",latency.wAvgTranRTT); + PrintConsoleString("wDevTranRTT %d.",latency.wDevTranRTT);*/ + return latency.avgLinkRTT*0.001; + } + return 0; +} + +//Host a new Network Game +void HostNetGame(int maxPlayers,char *pass) +{ + HandleError(NT_SessionRestart(gSessionRef,maxPlayers,kGameName,pass)); + gTerminateSession=false; +} + +void NetworkDisconnectPlayer(int netID,UInt8 type) +{ + NT_MemberInfo info; + if(NT_GetMemberInfo(gSessionRef,netID,&info)) + { + if(type!=kNetworkDisconnectLicenseCopies) + HandleError(NT_SessionBan(gSessionRef,&info.address,false,NULL)); + NetworkSendPacket(kMessageTypeKicked,&type,sizeof(UInt8),kMessagePriorityHigh,netID); + } +} + +void NetworkChangePassword(char *pass) +{ + HandleError(NT_SetSessionPassword(gSessionRef,pass)); +} + +//join a network game at the ip address given in the string address. +//returns an error code or 0. passes the player's id in id. +int JoinNetGame(char *address,char *errorString) +{ + if(!*address) + { + sprintf(errorString,"No Address Entered."); + return false; + } + //PrintConsoleString(address); + NetworkAddress addy; + Result32 err=SimpleResolve(eNetworkStackTCPIP,address,&addy); + if(err) + { + sprintf(errorString,"Can't resolve host. (%d)",err); + return false; + } + + gTerminateSession=false; + NetworkClearPacketQueue(); + HandleError(NT_SessionRejoin(gSessionRef,&addy,kGameName,NULL,&err)); + + if(err==eNetworkToolErrorBadPass) + { + err=0; + char password[256]; + if(InterfaceGetUserInputString("Please enter Game Password",password,256,true,true)) + { + NetworkClearPacketQueue(); + HandleError(NT_SessionRejoin(gSessionRef,&addy,kGameName,password,&err)); + } + else + return false; + } + if(err) + { + switch(err) + { + case eNetworkToolErrorNoServer: + sprintf(errorString,"No Server Running on that Machine",err); + break; + case eNetworkToolErrorTimeout: + sprintf(errorString,"Network_Tool Timeout",err); + break; + case eNetworkToolErrorLatency: + sprintf(errorString,"Latency is too high.",err); + break; + case eNetworkToolErrorBanned: + sprintf(errorString,"You are banned from this host.",err); + break; + case eNetworkToolErrorTooMany: + sprintf(errorString,"The game is full.",err); + break; + case eNetworkToolErrorBadPass: + sprintf(errorString,"Wrong Password",err); + break; + case eNetworkToolErrorLocked: + sprintf(errorString,"Can't join in the middle of a game.",err); + break; + default: + sprintf(errorString,"Connection Error. (%d)",err); + } + return false; + } + + float startTime=TimeGetSeconds(); + + while(TimeGetSeconds()kNetworkToolPacketSize-1) + FailWithErrorString("Packet too large."); + + NT_MemberInfo info; + NT_GetSelfMemberInfo(gSessionRef,&info); + NT_MemberListType self=0x00000001<kCompressionSize&&gAllowCompression) + { + int oldsize=size; + if(CompressPacket(data+1,&size)==eCommonErrorNone) + if(oldsizewhat=kMessageTypeGameTerminated; + packet->data=NULL; + return true; + } + else if(QStubPtr rcv=QueueRemove(&gPacketQueue)) + { + *packet=((tQueuedPacket*)rcv)->packet; + if(packet->what&kCompressionFlag) + { + DecompressPacket(packet->data,&packet->size); + packet->what^=kCompressionFlag; + } + MemoryFreeBlock(rcv); + return true; + } + if(gReggieConnected) + ReggieMessageCheck(gReggieRef); + return false; +} + +void NetworkDisposePacket(tNetworkPacket* packet) +{ + if(packet->data) + MemoryFreeBlock(packet->data); +} + +void NetworkInit() +{ + HandleError(NT_Open()); + QueueCreate(&gPacketQueue); +} + +void NetworkIdle() +{ + static float lastStatus=0; + NT_Idle(); + if(gSessionRef) + { + /* if(TimeGetSeconds()-lastStatus<0.5) + return; + lastStatus=TimeGetSeconds(); + char str[16]; + NetworkGetStatusString(str); + // printf("%s\n",str); + NT_MemberInfo info; + NT_GetSelfMemberInfo(gSessionRef,&info); + if(info.member) + { + if(gLastRTT<0) + printf("No Ping Response since %f seconds.\n",lastStatus-gLastPingReceived); + else + { + //printf("Last RTT: %f\n",gLastRTT); + gLastRTT=-1; + } + NetworkSendPacket(kMessageTypePing,&lastStatus,sizeof(float),kMessagePriorityLow,kMessageSendToHostOnly); + }*/ + } +} + +void NetworkExit() +{ + if(gSessionRef) + NetworkSendPacket(kMessageTypeBye,0,NULL,kMessagePriorityHigh,kMessageSendToAll); + if(gReggieConnected) + ReggieDispose(gReggieRef,true); + NT_Close(); + while(QStubPtr rcv=QueueRemove(&gPacketQueue)) + MemoryFreeBlock(rcv); + QueueEmpty(&gPacketQueue); +} + +//wait for a response from all players, to synchronize the start of the game. +int NetworkSynch(int numPlayers) +{ + //PrintConsoleString("Synching %d...",numPlayers); + + NetworkSendPacket(kMessageTypeSynch,nil,0,kMessagePriorityHigh,kMessageSendToAll); + UInt32 lastSynch=0; + UInt32 startClock; + int start=false; + tNetworkPacket leftMessage[kMaxPlayers]; + int numLeftMessages=0; + + int synchMessagesReceived=0; + while(synchMessagesReceived +#include +#include +#include +#include "network.h" +#include "entities.h" +#include "vectors.h" +#include "carphysics.h" +#include "particles.h" +#include "tracks.h" +#include "gameframe.h" +#include "networkphysics.h" +#include "log.h" +#include "error.h" +#include "gameinitexit.h" +#include "gamemem.h" +#include "gamesound.h" +#include "gametime.h" +#include "interfacemultiplayer.h" +#include "collision.h" +#include "environment.h" + +int gReplayIndex=0; +int gSynchsRecevied=0; + +//compressed subset of tWheelPhysics for network broadcasts +typedef struct{ + unsigned char rotation,glow; + char angularVelo; + char slipVelo; + char surfaceType; +} tWheelNetPhysics; + +//compressed subset of tCarPhysics for network broadcasts +typedef struct{ + unsigned char lightFlags; + unsigned char rpm,throttle; + unsigned char shiftDelay,handbrake; + unsigned short damage; + char steering; + tWheelNetPhysics wheels[kMaxWheels]; + tVector3 cameraPos; +} tCarNetPhysics; + +//a structure containing all the physics data necessary +//to transmit a cars state for network broadcasts +typedef struct{ + tPhysicsMessage entity; + tCarNetPhysics phys; +} tCarPhysicsMessage; + +typedef struct{ + tPhysicsMessage entity; + char resends; + char filler[sizeof(tCarNetPhysics)-1]; +} tSolidPhysicsMessage; + + +#define kMaxGameChatMessages 4 +#define kMaxOutboxSize (kNetworkToolPacketSize/sizeof(tCarPhysicsMessage)) +int gNumGameChatMessages=0; +tGameChatMessage gGameChatMessagesRecv[kMaxGameChatMessages]; +tCarPhysicsMessage gHostOutbox[kMaxOutboxSize]; +int gOutboxPos=0,gOutboxSender=-1; +float gLastOutboxSend=0; + +void GameChatMessagesScroll() +{ + for(int i=0;i=kMaxGameChatMessages) + GameChatMessagesScroll(); + gGameChatMessagesRecv[gNumGameChatMessages].timestamp=TimeGetSeconds(); + strcpy(gGameChatMessagesRecv[gNumGameChatMessages].message,message); + gNumGameChatMessages++; +} + +void InsertToOutbox(tCarPhysicsMessage *msg,int from) +{ + for(int i=0;ientity.id) + { + int mFrame=msg->entity.frame; + int bFrame=gHostOutbox[i].entity.frame; + S32Swap(mFrame); + S32Swap(bFrame); + if(bFramex=dirZ.x*(1/32767.0); + MatrixGetZVector(m)->y=dirZ.y*(1/32767.0); + MatrixGetZVector(m)->z=dirZ.z*(1/32767.0); + MatrixGetYVector(m)->x=dirY.x*(1/32767.0); + MatrixGetYVector(m)->y=dirY.y*(1/32767.0); + MatrixGetYVector(m)->z=dirY.z*(1/32767.0); + *MatrixGetXVector(m)=*MatrixGetYVector(m)%*MatrixGetZVector(m); +} + +//compresses generic entity physics data into a tPhysicsMessage +tVector3 PreparePhysicsMessage(tGameEntity *entity,tPhysicsMessage *message) +{ + message->prioritized=false; + message->id=entity->id; + message->pos=entity->pos; + tVector3 v=Vector(0,0,0),a=Vector(0,0,0); + tVector3 rz=Vector(0,0,0),ry=Vector(0,0,0); + for(int i=0;i<8;i++)//smooth suspension inbalance + { + rz=rz+*MatrixGetZVector(entity->lastRVelos[i]); + ry=ry+*MatrixGetYVector(entity->lastRVelos[i]); + a=a+entity->lastAccel[i]; + v=v+entity->lastVelos[i]; + } + a=a*1.0/8; + + v=entity->velo; + v.y=0; + a.y=0; + for(int i=0;i<16;i++) + v.y+=entity->lastVelos[i].y; + for(int i=0;i<32;i++) + a.y+=entity->lastAccel[i].y; + v.y/=16; + a.y/=32; + + if(entity->physicsType==kPhysicsTypeSolid) + { + rz=*MatrixGetZVector(entity->rVelo); + ry=*MatrixGetYVector(entity->rVelo); + v=entity->velo; + } + + //if(sqr(v)>sqr(3)) + message->velo.x=v.x*255; + message->velo.y=v.y*255; + message->velo.z=v.z*255; + if(sqr(entity->accel)accel; + message->accel.x=a.x*255; + message->accel.y=a.y*255; + message->accel.z=a.z*255; + message->frame=gFrameCount; + + tVector3 z=*MatrixGetZVector(entity->dir); + message->dirZ.x=fabs(z.x)<1?z.x*32767:sign(z.x)*32767; + message->dirZ.y=fabs(z.y)<1?z.y*32767:sign(z.y)*32767; + message->dirZ.z=fabs(z.z)<1?z.z*32767:sign(z.z)*32767; + + tVector3 y=*MatrixGetYVector(entity->dir); + message->dirY.x=fabs(y.x)<1?y.x*32767:sign(y.x)*32767; + message->dirY.y=fabs(y.y)<1?y.y*32767:sign(y.y)*32767; + message->dirY.z=fabs(y.z)<1?y.z*32767:sign(y.z)*32767; + + rz=!Vector(rz.x,0,rz.z); + ry=Vector(0,1,0); +/* rz=!rz; + tVector3 rx=!(ry%rz); + ry=!(rz%rx);*/ + + message->rVeloZ.x=fabs(rz.x)<1?rz.x*32767:sign(rz.x)*32767; + message->rVeloZ.y=fabs(rz.y)<1?rz.y*32767:sign(rz.y)*32767; + message->rVeloZ.z=fabs(rz.z)<1?rz.z*32767:sign(rz.z)*32767; + + message->rVeloY.x=fabs(ry.x)<1?ry.x*32767:sign(ry.x)*32767; + message->rVeloY.y=fabs(ry.y)<1?ry.y*32767:sign(ry.y)*32767; + message->rVeloY.z=fabs(ry.z)<1?ry.z*32767:sign(ry.z)*32767; + return rz; +} + + +#define kMinPacketDelay 5 +#define kMaxPacketDelay 30 +#define kMaxPacketRVeloDiff 0.005 + +float RVeloDiff(tMatrix3 v1,tMatrix3 v2) +{ + float result=~(*MatrixGetXVector(v1)-*MatrixGetXVector(v2)); + result+=~(*MatrixGetYVector(v1)-*MatrixGetYVector(v2)); + result+=~(*MatrixGetZVector(v1)-*MatrixGetZVector(v2)); + return result; +} + +float RemoteCarDistance(tGameEntity *entity) +{ + float minDist=INFINITY; + for(int i=0;inumPlayers;i++) + if(gCarEntities[i]->physicsMachine==kPhysicsRemote) + { + float dist=sqr(entity->pos-gCarEntities[i]->remoteCameraPos); + if(distlastPacketSent; + float dist=RemoteCarDistance(entity)*0.001; + float score=0; + float veloChangePerc; + if(~entity->velo>1) + { + veloChangePerc=~(entity->velo-entity->lastVeloSent)/~entity->velo; + /*if(veloChangePerc>0.3&&dist<0.25&&~(entity->velo-entity->lastVeloSent)>1) + { + //printf("fp\n"); + return 2; + }*/ + } + else + veloChangePerc=~(entity->velo-entity->lastVeloSent); + score+=veloChangePerc; + score+=~(rz-entity->lastRZSent)*kFPS; + score+=~(entity->lastDirZSent-*MatrixGetZVector(entity->dir)); + score*=lastPacket; + score-=dist; + if(lastPacket>kFPS*0.5) + return 1; + else if(score<0.6||lastPacket<5) + return 0; + else + return 1; +} + +int SendNewPacketLog(tGameEntity *entity,tVector3 rz) +{ + int lastPacket=gFrameCount-entity->lastPacketSaved; + float score=0; + float veloChangePerc; + if(~entity->velo>1) + { + veloChangePerc=~(entity->velo-entity->lastVeloSaved)/~entity->velo; + /*if(veloChangePerc>0.3&&dist<0.25&&~(entity->velo-entity->lastVeloSent)>1) + { + //printf("fp\n"); + return 2; + }*/ + } + else + veloChangePerc=~(entity->velo-entity->lastVeloSaved); + score+=veloChangePerc; + score+=~(rz-entity->lastRZSaved)*kFPS; + score+=~(entity->lastDirZSaved-*MatrixGetZVector(entity->dir)); + score*=lastPacket; + if(lastPacket>kFPS*0.5) + return 1; + else if(score<0.4||lastPacket<3) + return 0; + else + return 1; +} + + +void PhysicsMessageSwap(tPhysicsMessage* m) +{ + S32Swap(m->frame); + F32Swap(m->pos.x); + F32Swap(m->pos.y); + F32Swap(m->pos.z); + S16Swap(m->id); + S16Swap(m->velo.x); + S16Swap(m->velo.y); + S16Swap(m->velo.z); + S16Swap(m->accel.x); + S16Swap(m->accel.y); + S16Swap(m->accel.z); + S16Swap(m->dirZ.x); + S16Swap(m->dirZ.y); + S16Swap(m->dirZ.z); + S16Swap(m->dirY.x); + S16Swap(m->dirY.y); + S16Swap(m->dirY.z); + S16Swap(m->rVeloZ.x); + S16Swap(m->rVeloZ.y); + S16Swap(m->rVeloZ.z); + S16Swap(m->rVeloY.x); + S16Swap(m->rVeloY.y); + S16Swap(m->rVeloY.z); +} + +//sends out a car's state to other players (and logs it for the replay log) +void SendCarPhysicsMessage(tGameEntity *entity) +{ + tCarPhysics *phys=(tCarPhysics*)entity->physics; + tCarPhysicsMessage message; + + //compress position, etc.. + tVector3 rz=PreparePhysicsMessage(entity,&message.entity); + + //compress car data + message.phys.lightFlags=phys->lightFlags; + message.phys.handbrake=phys->handbrake*255; + float shiftDelay=gFrameCount*kFrameTime-phys->lastGearSwitch; + message.phys.shiftDelay=(shiftDelay<1)?shiftDelay*255:255; + message.phys.rpm=phys->rpm*(255.0/10000.0); + message.phys.throttle=phys->throttle*255; +// message.phys.brake=phys->brake*255; +// message.phys.gear=phys->gear; + message.phys.steering=fabs(phys->steering)<1?phys->steering*127:sign(phys->steering)*127; + message.phys.damage=phys->damage; + U16Swap(message.phys.damage); + message.phys.cameraPos=gCameraEntity->pos; + F32Swap(message.phys.cameraPos.x); + F32Swap(message.phys.cameraPos.y); + F32Swap(message.phys.cameraPos.z); + for(int i=0;icar.numWheels;i++) + { + message.phys.wheels[i].rotation=phys->wheels[i].rotation*(255.0/(2*PI)); + message.phys.wheels[i].slipVelo=phys->wheels[i].slipVelo; + message.phys.wheels[i].surfaceType=phys->wheels[i].surfaceType; + message.phys.wheels[i].glow=phys->wheels[i].glow*255.0; + if(fabs(phys->wheels[i].angularVelo)<127) + message.phys.wheels[i].angularVelo=phys->wheels[i].angularVelo; + else + message.phys.wheels[i].angularVelo=127*sign(phys->wheels[i].angularVelo); + } + for(int i=phys->car.numWheels;ilastPacketSaved=gFrameCount; + entity->lastVeloSaved=entity->velo; + entity->lastRZSaved=rz; + entity->lastDirZSaved=*MatrixGetZVector(entity->dir); + + } + //broadcast packet over network + if(gGameInfo->network) + if(int prio=SendNewPacket(entity,rz)) + { + if(prio==2) + message.entity.prioritized=true; + if(gGameInfo->playerID) + NetworkSendPacket(kMessageTypePhysics,&message,sizeof(tCarPhysicsMessage),kMessagePriorityNormal,kMessageSendToHostOnly); + else + InsertToOutbox(&message,0); + entity->lastPacketSent=gFrameCount; + entity->lastVeloSent=entity->velo; + entity->lastRZSent=rz; + entity->lastDirZSent=*MatrixGetZVector(entity->dir); + } +} + +void SendSolidPhysicsMessage(tGameEntity *entity,int resends) +{ + static int lastRockUpdate=0; + if(entity->lastFrame==gFrameCount&&gFrameCount>0) + { + tSolidPhysicsMessage message; + for(int i=0;inetwork) + if(gGameInfo->playerID) + { + if(gFrameCountlastRockUpdate+kFPS*0.25) + { + NetworkSendPacket(kMessageTypePhysics,&message,sizeof(tSolidPhysicsMessage),kMessagePriorityHigh,kMessageSendToHostOnly); + lastRockUpdate=gFrameCount; + } + else + entity->lastFrame++; + } + else + InsertToOutbox((tCarPhysicsMessage*)&message,0); + } +} + +void AddRockUpdateToOutbox() +{ + tGameEntity *entity=(tGameEntity*)gFirstEntity->next; + if(gOutboxPos==kMaxOutboxSize) + return; + while(entity!=gFirstEntity) + { + if(entity->physicsType==kPhysicsTypeSolid) + { + tSolidEntityPhysics *ent=(tSolidEntityPhysics*)FileGetParsedDataPtr(entity->physicsData,kParserTypeSolidEntityDesc,sizeof(tSolidEntityPhysics)); + if(ent->movable&&ent->mass>=kSolidEntityNetworkMass) + if(entity->lastActivitylastFramenumPlayers;i++) + if(sqr(gCarEntities[i]->pos-entity->pos)pos-entity->pos); + if(minDist>sqr(80)) + { + entity->lastFrame=gFrameCount; + SendSolidPhysicsMessage(entity,0); + return; + } + } + } + entity=(tGameEntity*)entity->next; + } +} + +void CleanOutbox() +{ + for(int i=0;inext; + + //see if we find the entity it belongs to + while(entity!=gFirstEntity&&iid==id) + { + if(entity->physicsType==kPhysicsTypeSolid) + { + if(((tSolidPhysicsMessage*)gHostOutbox+i)->resends==0||((tSolidPhysicsMessage*)gHostOutbox+i)->resends>3) + { + memmove(gHostOutbox+i,gHostOutbox+i+1,sizeof(tSolidPhysicsMessage)*(gOutboxPos-i-1)); + gOutboxPos--; + entity=gFirstEntity; + + } + else + ((tSolidPhysicsMessage*)gHostOutbox+i)->resends--; + } + else + { + memmove(gHostOutbox+i,gHostOutbox+i+1,sizeof(tSolidPhysicsMessage)*(gOutboxPos-i-1)); + gOutboxPos--; + entity=gFirstEntity; + } + } + entity=(tGameEntity*)entity->next; + } + } +} + +//sends out physics information for this frame +void NetworkSendPhysics() +{ + tGameEntity *entity=(tGameEntity*)gFirstEntity->next; + while(entity!=gFirstEntity) + { + if(entity->physicsMachine==kPhysicsLocal) + { + switch(entity->physicsType) + { + case kPhysicsTypeCar: + SendCarPhysicsMessage(entity); + break; + + case kPhysicsTypeSolid: + SendSolidPhysicsMessage(entity,2); + break; + } + } + entity=(tGameEntity*)entity->next; + } + float t=TimeGetSeconds(); + if(gGameInfo->playerID==0&&gGameInfo->network) + { + int fastPath=false; + for(int i=0;i0&&(gLastOutboxSendpos=Vector(-10000,-10000,-10000); + ent->netPos=Vector(-10000,-10000,-10000); + ent->velo=Vector(0,0,0); + ent->id=-1; + tCarPhysics *phys=(tCarPhysics*)ent->physics; + phys->position=0; + phys->checkPoint=0; + for(int i=0;ilapTimes[i]=0; + gGameInfo->netID[i]=-1; + gGameInfo->playerCarNames[i][0]='\0'; + int numPlayers=0; + for(int i=0;inumPlayers;i++) + if(gCarEntities[i]->id>=0) + numPlayers++; + if(numPlayers==1) + { + tCarPhysics *lastPhys=(tCarPhysics*)gCarEntities[0]; + lastPhys->lead=0; + } + + if(gReplayViewedEntityID==i) + { + gViewedEntity=gCarEntities[0]; + gReplayViewedEntityID=0; + } +} + +//receive physics data from other players on the network +void NetworkReceivePhysics() +{ + if(gGameInfo->network) + { + tNetworkPacket message; + int exit=false; + while(NetworkReceivePacket(&message)) + { + switch(message.what) + { + case kMessageTypePhysics: + if(gGameInfo->playerID==0) + InsertToOutbox((tCarPhysicsMessage*)message.data,message.from); + //we have received a physics state update + for(int i=0;iid-gCarEntities[0]->id,physMessage->frame,gFrameCount); + + tGameEntity *entity=(tGameEntity*)gFirstEntity->next; + + //see if we find the entity it belongs to + while(entity!=gFirstEntity) + { + //is this the entity the message refers to? + if(entity->id==physMessage->id&&entity!=gCarEntities[gGameInfo->playerID]) + //is this the most recent physics update for the entity? + //(we want to avoid packets coming in in the wrong order messung up everything) + if(entity->lastFrameframe)//&&entity->lastCollFrame<=physMessage->frame) + { + //entity->lastCollFrame=0; + //decompress physics info + entity->netPos=physMessage->pos; + entity->netVelo.x=physMessage->velo.x/255.0; + entity->netVelo.y=physMessage->velo.y/255.0; + entity->netVelo.z=physMessage->velo.z/255.0; + entity->collVelo=entity->netVelo; + entity->accel.x=physMessage->accel.x/255.0; + entity->accel.y=physMessage->accel.y/255.0; + entity->accel.z=physMessage->accel.z/255.0; + + entity->lastFrame=physMessage->frame; + if(entity->lastFrame>gFrameCount) + entity->lastFrame=gFrameCount; + ShortVectorsToMatrix(physMessage->dirZ,physMessage->dirY,entity->netDir); + ShortVectorsToMatrix(physMessage->rVeloZ,physMessage->rVeloY,entity->rVelo); + + //is this a car? + if(entity->physicsType==kPhysicsTypeCar) + { + //decompress car physics info + tCarPhysics *phys=(tCarPhysics*)entity->physics; + tCarNetPhysics *inPhys=&(((tCarPhysicsMessage*)message.data)+i)->phys; + phys->lightFlags=inPhys->lightFlags; + phys->rpm=inPhys->rpm/(255.0/10000.0); + phys->throttle=inPhys->throttle/255.0; + phys->steering=inPhys->steering/127.0; + phys->handbrake=inPhys->handbrake/255.0; +// phys->brake=inPhys->brake/255.0; +// phys->gear=inPhys->gear; + phys->lastGearSwitch=gFrameCount*kFrameTime-inPhys->shiftDelay/255.0; + unsigned short damage=inPhys->damage; + U16Swap(damage); + phys->damage=damage; + + for(int i=0;iwheels[i].rotation=inPhys->wheels[i].rotation/(255.0/(2*PI)); + phys->wheels[i].slipVelo=inPhys->wheels[i].slipVelo; + phys->wheels[i].surfaceType=inPhys->wheels[i].surfaceType; + phys->wheels[i].glow=inPhys->wheels[i].glow/255.0; + phys->wheels[i].angularVelo=inPhys->wheels[i].angularVelo; + } + entity->remoteCameraPos=inPhys->cameraPos; + F32Swap(entity->remoteCameraPos.x); + F32Swap(entity->remoteCameraPos.y); + F32Swap(entity->remoteCameraPos.z); + + //dead recogning + int frameDiff=gFrameCount-physMessage->frame; + //entity->netPos=entity->netPos+entity->netVelo*kFrameTime*frameDiff+entity->accel*kFrameTime*kFrameTime*frameDiff*frameDiff*0.5; + //entity->netVelo=entity->netVelo+entity->accel*kFrameTime*frameDiff; + int collIndex=kNumLastCollisions-1; + while(frameDiff>0) + { + while(phys->lastCollisions[collIndex].frameCountregData=NULL; + if(phys->lastCollisions[collIndex].frameCount==gFrameCount-frameDiff) + ApplyImpulse(entity,phys->lastCollisions[collIndex].attackPoint,phys->lastCollisions[collIndex].veloDiff,phys->lastCollisions[collIndex].rotationFactor,true); + entity->netPos=entity->netPos+entity->netVelo*kFrameTime; + entity->netVelo=entity->netVelo+entity->accel*kFrameTime; + MatrixMult(entity->netDir,entity->rVelo,entity->netDir); + frameDiff--; + } + } + else if(entity->physicsType==kPhysicsTypeSolid) + { + entity->pos=entity->netPos; + entity->velo=entity->netVelo; + MatrixCopy(entity->netDir,entity->dir); + + int frameDiff=gFrameCount-physMessage->frame; + while(frameDiff>0) + { + if(entity->lastActivity+kFPS>gFrameCount-frameDiff) + { + entity->velo.y-=gEnvironment->gravity*kFrameTime; + float dampfactor=0; + float v=~entity->velo; + if(v<7)dampfactor=(7-v)/7; + *MatrixGetXVector(entity->rVelo)=Vector(1,0,0)+(1-dampfactor*kFrameTime)*(*MatrixGetXVector(entity->rVelo)-Vector(1,0,0)); + *MatrixGetYVector(entity->rVelo)=Vector(0,1,0)+(1-dampfactor*kFrameTime)*(*MatrixGetYVector(entity->rVelo)-Vector(0,1,0)); + *MatrixGetZVector(entity->rVelo)=Vector(0,0,1)+(1-dampfactor*kFrameTime)*(*MatrixGetZVector(entity->rVelo)-Vector(0,0,1)); + entity->velo=(1-dampfactor*kFrameTime)*entity->velo; + + entity->pos=entity->pos+entity->velo*kFrameTime; + MatrixMult(entity->dir,entity->rVelo,entity->dir); + } + MatrixReAdjust(entity->rVelo); + + //Check if the entity is moving, and update lastActivity + if(entity->velo*entity->velo>kMinActivityVelo*kMinActivityVelo) + entity->lastActivity=gFrameCount-frameDiff; + + if(entity->lastActivity+kFPS>gFrameCount-frameDiff) + if(!((gFrameCount-frameDiff)% kSolidCollisionRate)) + SolidCheckCollision(entity); + frameDiff--; + } + } + + /*if(physMessage->prioritized) + { + entity->pos=entity->netPos; + entity->velo=entity->netVelo; + MatrixCopy(entity->netDir,entity->dir); + }*/ + } + entity=(tGameEntity*)entity->next; + } + } + break; + + case kMessageTypeChat: + InsertGameChatMessage(((tChatMessage*)message.data)->str); + ChatBufferInsert(((tChatMessage*)message.data),gChatBuffer); + if(((tChatMessage*)message.data)->flags&(kChatFlagSystem|kChatFlagAlert)) + PlayInterfaceSound(FileGetReference("systemchat.wav")); + else + PlayInterfaceSound(FileGetReference("chat.wav")); + break; + + case kMessageTypeBye: + { + if(message.from==0) + sprintf(gDisconnectString,"%s quit and stopped hosting.",gGameInfo->playerNames[0]); + int netID=message.from; + int id=-1; + //find the players id + for(int i=0;inumNetPlayers;i++) + if(gGameInfo->netID[i]==netID) + id=i; + + if(id!=-1) + { + gGameInfo->playerVersions[id]=-2; + /*tChatMessage m; + m.flags=kChatFlagSystem; + sprintf(m.str,"### %s is quitting.",gGameInfo->playerNames[id]); + SoundReInit(); + PlayInterfaceSound(FileGetReference("systemchat.wav")); + InsertGameChatMessage(m.str); + ChatBufferInsert(&m,gChatBuffer);*/ + } + } + break; + + case kMessageTypeKicked: + sprintf(gDisconnectString,"You have been disconnected by the host."); + case kMessageTypeGameTerminated: + gGameEnd=kEndGame; + exit=true; + break; + + case kMessageTypeEndGame: + gGameEnd=kEndGameNoLeave; + break; + + case kMessageTypePause: + if(gNetPauseTime==0) + { + gNetPauseTime=*(int*)message.data; + S32Swap(gNetPauseTime); + } + break; + + case kMessageTypeSynch: + gSynchsRecevied++; + break; + + case kMessageTypeResume: + if(gPaused) + { + gNetPauseTime=0; + for(int i=1;inumNetPlayers;i++) + if(gGameInfo->netID[i]==-1) + gSynchsRecevied++; + NetworkSynch(gGameInfo->numNetPlayers-gSynchsRecevied); + gSynchsRecevied=0; + UnPauseGame(); + } + break; + + case kMessageTypePlayerLeft: + { + int netID=message.from; + int id=-1; + //find the players id + for(int i=0;inumPlayers;i++) + if(gGameInfo->netID[i]==netID) + id=i; + if(id!=-1) + { + tChatMessage m; + m.flags=kChatFlagSystem; + if(gGameInfo->playerVersions[id]!=-2) + sprintf(m.str,"### %s is disconnected due to network problems.",gGameInfo->playerNames[id]); + else + sprintf(m.str,"### %s quit the game.",gGameInfo->playerNames[id]); + SoundReInit(); + PlayInterfaceSound(FileGetReference("systemchat.wav")); + InsertGameChatMessage(m.str); + ChatBufferInsert(&m,gChatBuffer); + KillPlayer(id); + } + } + break; + + case kMessageTypeID: + NetworkQueuePacket(&message); + exit=true; + break; + + case kMessageTypeFinishTime: + { + tFinishTimeMessage *m=(tFinishTimeMessage*)message.data; + S32Swap(m->time); + S32Swap(m->player); + //printf("%d,%d\n",m->time,m->player); + tCarPhysics *phys=(tCarPhysics*)gCarEntities[m->player]->physics; + phys->finishTime=m->time; + } + break; + + case kMessageTypePlayerJoined: + if(gGameInfo->demolition||gGameInfo->numNetPlayers<=1) + { + NetworkQueuePacket(&message); + gGameEnd=kEndGameNoLeave; + exit=true; + } + else + { + UInt8 reason=kJoinDeniedInProgress; + NetworkSendPacket(kMessageTypeJoinDenied,&reason,sizeof(UInt8),kMessagePriorityHigh,kMessageSendToAllButSelf); + } + break; + } + + + //release the message. + NetworkDisposePacket(&message); + + if(exit) + return; + } + } +} + +#define kMaxLookAhead 0.5 +//similar to NetworkReceivePhysics but does not wait for packets from the network, +//instead it processes packets in the replay log. +void LogReplayFrame() +{ + int type,size; + char message[1024]; + while(LogGetPacket(gReplayIndex,&type,&size,gGameInfo->numLaps==-1?kLogGhostLog:kLogReplayLog,message)) + { + switch(type) + { + case kMessageTypePhysics: + { + tPhysicsMessage *physMessage=(tPhysicsMessage*)((char*)message); + PhysicsMessageSwap(physMessage); + //if(physMessage->idid) + // physMessage->id=gCarEntities[0]->id; + + if(physMessage->frame>gFrameCount)return; + tGameEntity *entity=(tGameEntity*)gFirstEntity->next; + //see if we find the entity it belongs to + while(entity!=gFirstEntity) + { + //is this the entity the message refers to? + if(entity->id==physMessage->id) + { + //decompress physics info + entity->pos=physMessage->pos; + entity->velo.x=physMessage->velo.x/255.0; + entity->velo.y=physMessage->velo.y/255.0; + entity->velo.z=physMessage->velo.z/255.0; + entity->accel.x=physMessage->accel.x/255.0; + entity->accel.y=physMessage->accel.y/255.0; + entity->accel.z=physMessage->accel.z/255.0; +// ShortVectorsToMatrix(physMessage->dirZ,physMessage->dirY,entity->dir); + ShortVectorsToMatrix(physMessage->dirZ,physMessage->dirY,entity->netDir); + ShortVectorsToMatrix(physMessage->rVeloZ,physMessage->rVeloY,entity->rVelo); + + //is this a car? + if(entity->physicsType==kPhysicsTypeCar) + { + //decompress car physics info + tCarPhysics *phys=(tCarPhysics*)entity->physics; + tCarNetPhysics *inPhys=&((tCarPhysicsMessage*)message)->phys; + phys->lightFlags=inPhys->lightFlags; + phys->rpm=inPhys->rpm/(255.0/10000.0);; + phys->throttle=inPhys->throttle/255.0; + phys->steering=inPhys->steering/127.0; + phys->handbrake=inPhys->handbrake/255.0; +// phys->brake=inPhys->brake/255.0; +// phys->gear=inPhys->gear; + phys->lastGearSwitch=gFrameCount*kFrameTime-inPhys->shiftDelay/255.0; + unsigned short damage=inPhys->damage; + U16Swap(damage); + phys->damage=damage; + + for(int i=0;iwheels[i].rotation=inPhys->wheels[i].rotation/(255.0/(2*PI)); + phys->wheels[i].slipVelo=inPhys->wheels[i].slipVelo; + phys->wheels[i].surfaceType=inPhys->wheels[i].surfaceType; + phys->wheels[i].glow=inPhys->wheels[i].glow/255.0; + phys->wheels[i].angularVelo=inPhys->wheels[i].angularVelo; + } + } + } + entity=(tGameEntity*)entity->next; + } + } + break; + } + gReplayIndex++; + } +} + +void LogReplayGhostFrame() +{ + int type,size; + char message[1024]; + while(LogGetPacket(gReplayIndex,&type,&size,kLogGhostLog,message)) + { + switch(type) + { + case kMessageTypePhysics: + { + tPhysicsMessage *physMessage=(tPhysicsMessage*)message; + PhysicsMessageSwap(physMessage); + + if(physMessage->frame>gFrameCount-gCurrentLapStart+gBestLapStart) + return; + + gGhostEntity->pos=physMessage->pos; + gGhostEntity->velo.x=physMessage->velo.x/255.0; + gGhostEntity->velo.y=physMessage->velo.y/255.0; + gGhostEntity->velo.z=physMessage->velo.z/255.0; + gGhostEntity->lastFrame=physMessage->frame; + ShortVectorsToMatrix(physMessage->dirZ,physMessage->dirY,gGhostEntity->dir); + + tCarPhysics *phys=(tCarPhysics*)gGhostEntity->physics; + tCarNetPhysics *inPhys=(tCarNetPhysics*)((char*)message+sizeof(tPhysicsMessage)); + phys->lightFlags=inPhys->lightFlags; + phys->rpm=inPhys->rpm/(255.0/10000.0);; + phys->throttle=inPhys->throttle/255.0; + phys->steering=inPhys->steering/127.0; + for(int i=0;iwheels[i].rotation=inPhys->wheels[i].rotation/(255.0/(2*PI)); + phys->wheels[i].slipVelo=inPhys->wheels[i].slipVelo; + phys->wheels[i].surfaceType=inPhys->wheels[i].surfaceType; + } + } + break; + } + gReplayIndex++; + } + gGhostEntity->pos=Vector(10000,10000,10000); + gGhostEntity->velo=Vector(0,0,0); +} + diff --git a/source/networkphysics.h b/source/networkphysics.h new file mode 100644 index 0000000..f120591 --- /dev/null +++ b/source/networkphysics.h @@ -0,0 +1,42 @@ +#ifndef __NETWORKPHYSICS +#define __NETWORKPHYSICS + +#include "vectors.h" + +typedef struct{ + char x,y,z; +} tVector3Char; + +typedef struct{ + short x,y,z; +} tVector3Short; + +typedef struct{ + int frame; + tVector3 pos; + short id; + char prioritized; + tVector3Short velo; + tVector3Short accel; + tVector3Short dirZ,dirY; + tVector3Short rVeloZ,rVeloY; +} tPhysicsMessage; + +typedef struct{ + float timestamp; + char message[256]; +} tGameChatMessage; + +extern int gNumGameChatMessages; +extern tGameChatMessage gGameChatMessagesRecv[]; +extern int gReplayIndex; +extern int gOutboxPos; + +void InsertGameChatMessage(char *message); +void NetworkSendPhysics(); +void NetworkReceivePhysics(); +void LogReplayFrame(); +void LogReplayGhostFrame(); +void GameChatMessagesScroll(); + +#endif \ No newline at end of file diff --git a/source/notifications.mm b/source/notifications.mm new file mode 100644 index 0000000..7808a6d --- /dev/null +++ b/source/notifications.mm @@ -0,0 +1,81 @@ +#include +#include "gamesystem.h" +#include "gametime.h" +#include "screen.h" +#include "config.h" + +@interface Notifications : NSObject +{ +} +@end + +@implementation Notifications + +- (void)observerMethod:(NSNotification*)aNotification +{ + if(aNotification) + { + if([[aNotification name] compare:@"com.apple.iTunes.playerInfo"]==NSOrderedSame) + { + int oldState=giTunesPlaying; + NSDictionary *dic=[aNotification userInfo]; + NSString *str; + str=[dic objectForKey:@"Name"]; + if(str) + strcpy(giTunesSong,[str cString]); + else + strcpy(giTunesSong,""); + StripDiacritics(giTunesSong,strlen(giTunesSong),smSystemScript); + str=[dic objectForKey:@"Artist"]; + if(str) + strcpy(giTunesArtist,[str cString]); + else + strcpy(giTunesArtist,""); + StripDiacritics(giTunesArtist,strlen(giTunesArtist),smSystemScript); + str=[dic objectForKey:@"Player State"]; + if(str) + { + if([str compare:@"Playing"]==NSOrderedSame) + giTunesPlaying=kITunesPlaying; + else if([str compare:@"Paused"]==NSOrderedSame) + giTunesPlaying=kITunesPaused; + else + giTunesPlaying=kITunesStopped; + } + if(oldState!=giTunesPlaying||giTunesPlaying) + giTunesLastStatusUpdate=TimeGetSeconds(); + } + if([[aNotification name] compare:@"NSWorkspaceWillSleepNotification"]==NSOrderedSame) + { + if(!ScreenNoWindow()) + if(gConfig->fullscreen) + { + gConfig->fullscreen=false; + ScreenReInit(); + } + } + } +} + +- (id)init +{ + if(self==[super init]) + { + [[NSDistributedNotificationCenter defaultCenter] addObserver:self + selector:@selector(observerMethod:) + name:nil object:nil]; + [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self + selector:@selector(observerMethod:) + name:nil object:nil]; + } + return self; +} + +@end + +void InitITunesNotifications() +{ + NSAutoreleasePool *pool=[NSAutoreleasePool new]; + Notifications *notif; + notif = [Notifications new]; +} \ No newline at end of file diff --git a/source/parser.cpp b/source/parser.cpp new file mode 100755 index 0000000..366c825 --- /dev/null +++ b/source/parser.cpp @@ -0,0 +1,908 @@ +//parser.cpp +//File Parser for the various text-based file formats used by the game. + +#include +#include +#include "carphysics.h" +#include "fileio.h" +#include "vectors.h" +#include "gamemem.h" +#include "parser.h" +#include "roads.h" +#include "config.h" +#include "environment.h" +#include "gameinitexit.h" +#include "entities.h" +#include "text.h" +#include "challenges.h" +#include "rendercar.h" +#include "textures.h" +#include "texturesimport.h" +#include "controls.h" + +//These functions test if the string in verifyLabel matches the string in label, +//and if it does parses data into the specified format and stores the result in dst. +//Returns true if verifyLabel matches label and false otherwise. + +int TestForFloat(char *verifyLabel,char *label,char* data,float *dst) +{ + if(!_stricmp(verifyLabel,label)) + { + sscanf(data,"%f",dst); + return true; + } + else return false; +} + +int TestForInt(char *verifyLabel,char *label,char* data,int *dst) +{ + if(!_stricmp(verifyLabel,label)) + { + sscanf(data,"%d",dst); + return true; + } + else return false; +} + +int TestForHex(char *verifyLabel,char *label,char* data,int *dst) +{ + if(!_stricmp(verifyLabel,label)) + { + sscanf(data,"0x%x",dst); + return true; + } + else return false; +} + +int TestForVector3(char *verifyLabel,char *label,char* data,tVector3 *dst) +{ + if(!_stricmp(verifyLabel,label)) + { + sscanf(data,"{%f,%f,%f}",&dst->x,&dst->y,&dst->z); + return true; + } + else return false; +} + +int TestForVector2(char *verifyLabel,char *label,char* data,tVector2 *dst) +{ + if(!_stricmp(verifyLabel,label)) + { + sscanf(data,"{%f,%f}",&dst->x,&dst->y); + return true; + } + else return false; +} + +int TestForString(char *verifyLabel,char *label,char* data,char *dst) +{ + if(!_stricmp(verifyLabel,label)) + { + char* str=strchr(data,'"'); + if(str)str++; + else return false; + char* end=strchr(str,'"'); + if(end)*end='\0'; + else return false; + strcpy(dst,str); + return true; + } + else return false; +} + +//Stores a File Reference ID for the specified file name in data in dst +tFileRef TestForFile(char *verifyLabel,char *label,char* data,int *dst) +{ + if(!_stricmp(verifyLabel,label)) + { + char* fileName=strchr(data,'"'); + if(fileName)fileName++; + else return false; + char* endName=strchr(fileName,'"'); + if(endName)*endName='\0'; + else return false; + *dst=FileGetReference(fileName); + return true; + } + else return false; +} + + + + + +//This file contains one function ParseXXXDescLine for each of the +//different text-based file formats used by the game, which parses a +//string resembling one line of the file +//into the appropiate data fields of the file's structure in memory. + +int ParseRoadTypeDescLine(char *label,char *data,tRoadTypeList *types,int arrayPos,int arrayPos2) +{ + if(TestForInt("numTypes",label,data,&types->numTypes)){ + types->types=(tRoadType*)MemoryAllocateZeroedBlock((types->numTypes+1)*sizeof(tRoadType)); + return true; + } + + if(TestForInt("numVertices",label,data,&types->types[arrayPos].numVertices)){ + types->types[arrayPos].vertices=(tRoadTypeVertex*)MemoryAllocateZeroedBlock((types->types[arrayPos].numVertices+1)*sizeof(tRoadTypeVertex)); + return true; + } + if(TestForInt("endTopVertices",label,data,&types->types[arrayPos].endTopVertices))return true; + if(TestForInt("startTopVertices",label,data,&types->types[arrayPos].startTopVertices))return true; + + if(TestForInt("endShadow",label,data,&types->types[arrayPos].endShadow))return true; + if(TestForInt("startShadow",label,data,&types->types[arrayPos].startShadow))return true; + if(TestForFloat("minTrack",label,data,&types->types[arrayPos].minTrack))return true; + if(TestForFloat("maxTrack",label,data,&types->types[arrayPos].maxTrack))return true; + if(TestForFloat("reviveX",label,data,&types->types[arrayPos].reviveX))return true; + if(TestForFloat("reviveY",label,data,&types->types[arrayPos].reviveY))return true; + if(TestForFloat("traction",label,data,&types->types[arrayPos].traction))return true; + if(TestForString("name",label,data,types->types[arrayPos].name))return true; + + if(TestForFloat("texZoom",label,data,&types->types[arrayPos].vertices[arrayPos2].texZoom))return true; + if(TestForFile("texture",label,data,&types->types[arrayPos].vertices[arrayPos2].texture))return true; + if(TestForHex("materialFlags",label,data,&types->types[arrayPos].vertices[arrayPos2].materialFlags))return true; + if(TestForInt("surfaceType",label,data,&types->types[arrayPos].vertices[arrayPos2].surfaceType))return true; + if(TestForVector2("vertex1",label,data,&types->types[arrayPos].vertices[arrayPos2].vertex1))return true; + if(TestForVector2("vertex2",label,data,&types->types[arrayPos].vertices[arrayPos2].vertex2))return true; + if(TestForFloat("texCoord1",label,data,&types->types[arrayPos].vertices[arrayPos2].texCoord1))return true; + if(TestForFloat("texCoord2",label,data,&types->types[arrayPos].vertices[arrayPos2].texCoord2))return true; + + return false; +} + +int ParseSurfaceTypeDescLine(char *label,char *data,tSurfaceTypeList *types,int arrayPos) +{ + if(TestForInt("numTypes",label,data,&types->numTypes)){ + types->types=(tSurfaceType*)MemoryAllocateZeroedBlock((types->numTypes+1)*sizeof(tSurfaceType)); + return true; + } + + if(TestForFloat("grip",label,data,&types->types[arrayPos].grip))return true; + if(TestForFloat("slideGrip",label,data,&types->types[arrayPos].slideGrip))return true; + if(TestForFloat("bumpHeight",label,data,&types->types[arrayPos].bumpHeight))return true; + if(TestForFloat("bumpFreq",label,data,&types->types[arrayPos].bumpFreq))return true; + if(TestForInt("smokeEnable",label,data,&types->types[arrayPos].smokeEnable))return true; + if(TestForInt("trackEnable",label,data,&types->types[arrayPos].trackEnable))return true; + if(TestForInt("soundEnable",label,data,&types->types[arrayPos].soundEnable))return true; + if(TestForInt("sparksEnable",label,data,&types->types[arrayPos].sparksEnable))return true; + if(TestForInt("squeachEnable",label,data,&types->types[arrayPos].squeachEnable))return true; + if(TestForInt("reflectionEnable",label,data,&types->types[arrayPos].reflectionEnable))return true; + if(TestForInt("smokeStickEnable",label,data,&types->types[arrayPos].smokeStickEnable))return true; + if(TestForInt("soundEcho",label,data,&types->types[arrayPos].soundEcho))return true; + if(TestForFloat("brakeFactor",label,data,&types->types[arrayPos].brakeFactor))return true; + if(TestForFloat("minSmokeSlideVelo",label,data,&types->types[arrayPos].minSmokeSlideVelo))return true; + if(TestForFloat("maxSmokeSlideVelo",label,data,&types->types[arrayPos].maxSmokeSlideVelo))return true; + if(TestForFloat("minTrackSlideVelo",label,data,&types->types[arrayPos].minTrackSlideVelo))return true; + if(TestForFloat("maxTrackSlideVelo",label,data,&types->types[arrayPos].maxTrackSlideVelo))return true; + if(TestForFloat("minSkidPitchSlideVelo",label,data,&types->types[arrayPos].minSkidPitchSlideVelo))return true; + if(TestForFloat("maxSkidPitchSlideVelo",label,data,&types->types[arrayPos].maxSkidPitchSlideVelo))return true; + if(TestForFloat("minSkidGainSlideVelo",label,data,&types->types[arrayPos].minSkidGainSlideVelo))return true; + if(TestForFloat("maxSkidGainSlideVelo",label,data,&types->types[arrayPos].maxSkidGainSlideVelo))return true; + if(TestForFile("skidSound",label,data,&types->types[arrayPos].skidSound))return true; + if(TestForFile("smokeTexture",label,data,&types->types[arrayPos].smokeTexture))return true; + if(TestForFile("trackTexture",label,data,&types->types[arrayPos].trackTexture))return true; + if(TestForFloat("smokeSize",label,data,&types->types[arrayPos].smokeSize))return true; + if(TestForFloat("smokeGravity",label,data,&types->types[arrayPos].smokeGravity))return true; + if(TestForFloat("smokeSpeed",label,data,&types->types[arrayPos].smokeSpeed))return true; + if(TestForFloat("smokeSpread",label,data,&types->types[arrayPos].smokeSpread))return true; + if(TestForFloat("smokeMaxLife",label,data,&types->types[arrayPos].smokeMaxLife))return true; + if(TestForFloat("smokeMaxVelo",label,data,&types->types[arrayPos].smokeMaxVelo))return true; + + return false; +} + +int ParseEnvironmentDescLine(char *label,char *data,tEnvironment *env) +{ + if(TestForFile("sky0",label,data,&env->sky0))return true; + if(TestForFile("sky90",label,data,&env->sky90))return true; + if(TestForFile("sky180",label,data,&env->sky180))return true; + if(TestForFile("sky270",label,data,&env->sky270))return true; + if(TestForFile("skytop",label,data,&env->skytop))return true; + if(TestForFile("skybot",label,data,&env->skybot))return true; + if(TestForFile("spheremap",label,data,&env->spheremap))return true; + if(TestForFile("particlesType",label,data,&env->particlesType))return true; + if(TestForFile("surfaceTypes",label,data,&env->surfaceTypes))return true; + if(TestForFile("soundLoop",label,data,&env->soundLoop))return true; + if(TestForFile("soundRandom",label,data,&env->soundRandom))return true; + if(TestForFile("dirtMap",label,data,&env->dirtMap))return true; + if(TestForFile("altEnv",label,data,&env->altEnv))return true; + + if(TestForVector3("fogColor",label,data,&env->fogColor))return true; + if(TestForVector3("shadowColor",label,data,&env->shadowColor))return true; + if(TestForVector3("spotLightColor",label,data,&env->spotLightColor))return true; + if(TestForVector3("ambient",label,data,&env->ambient))return true; + if(TestForVector3("diffuse",label,data,&env->diffuse))return true; + if(TestForVector3("specular",label,data,&env->specular))return true; + if(TestForVector3("instrumentColor",label,data,&env->instrumentColor))return true; + if(TestForVector3("lightDir",label,data,&env->lightDir)){env->lightDir=!env->lightDir;return true;} + if(TestForVector3("flareDir",label,data,&env->flareDir)){env->flareDir=!env->flareDir;return true;} + if(TestForVector3("particlesVelo",label,data,&env->particlesVelo))return true; + if(TestForVector3("flashColor",label,data,&env->flashColor))return true; + + if(TestForVector2("particlesSize",label,data,&env->particlesSize))return true; + + if(TestForFloat("shadowIntensity",label,data,&env->shadowIntensity))return true; + if(TestForFloat("particlesAmount",label,data,&env->particlesAmount))return true; + if(TestForFloat("particlesVeloSpread",label,data,&env->particlesVeloSpread))return true; + if(TestForFloat("flashIntensity",label,data,&env->flashIntensity))return true; + if(TestForFloat("soundRandomProbility",label,data,&env->soundRandomProbility))return true; + if(TestForFloat("environmentMapIntensity",label,data,&env->environmentMapIntensity))return true; + if(TestForFloat("dirtIntensity",label,data,&env->dirtIntensity))return true; + if(TestForFloat("particlesLife",label,data,&env->particlesLife))return true; + if(TestForFloat("particlesHeight",label,data,&env->particlesHeight))return true; + + if(TestForFloat("gravity",label,data,&env->gravity))return true; + if(TestForFloat("airDensity",label,data,&env->airDensity))return true; + + if(TestForHex("envFlags",label,data,&env->envFlags))return true; + if(TestForInt("environmentMapping",label,data,&env->environmentMapping))return true; + if(TestForInt("shadowEnable",label,data,&env->shadowEnable))return true; + if(TestForInt("spotLightEnable",label,data,&env->spotLightEnable))return true; + if(TestForInt("particlesEnable",label,data,&env->particlesEnable))return true; + if(TestForInt("screenParticlesEnable",label,data,&env->screenParticlesEnable))return true; + if(TestForInt("soundLoopEnable",label,data,&env->soundLoopEnable))return true; + if(TestForInt("soundRandomEnable",label,data,&env->soundRandomEnable))return true; + if(TestForInt("flaresEnable",label,data,&env->flaresEnable))return true; + if(TestForInt("dirtEnable",label,data,&env->dirtEnable))return true; + if(TestForInt("hasAltEnv",label,data,&env->hasAltEnv))return true; + + if(TestForString("name",label,data,env->name))return true; + + return false; +} + +int ParseConfigDescLine(char *label,char *data,tSysConfig *config,int arrayPos) +{ + if(TestForInt("screenXSize",label,data,&config->screenXSize))return true; + if(TestForInt("screenYSize",label,data,&config->screenYSize))return true; + if(TestForInt("windowX",label,data,&config->windowX))return true; + if(TestForInt("windowY",label,data,&config->windowY))return true; + if(TestForInt("allCams",label,data,&config->allCams))return true; + if(TestForInt("useBetaBuilds",label,data,&config->useBetaBuilds))return true; + if(TestForInt("noGhost",label,data,&config->noGhost))return true; + if(TestForInt("onlyRegisteredPlayers",label,data,&config->onlyRegisteredPlayers))return true; + if(TestForInt("cantGoBackwards",label,data,&config->cantGoBackwards))return true; + if(TestForInt("fullscreen",label,data,&config->fullscreen))return true; + if(TestForInt("performanceStats",label,data,&config->performanceStats))return true; + if(TestForInt("stencil",label,data,&config->stencil))return true; + if(TestForInt("textureQuality",label,data,&config->textureQuality))return true; + if(TestForInt("textureFilter",label,data,&config->textureFilter))return true; + if(TestForInt("soundEnable",label,data,&config->soundEnable))return true; + if(TestForInt("maxCarSources",label,data,&config->maxCarSources))return true; + if(TestForInt("fsaa",label,data,&config->fsaa))return true; + if(TestForInt("ffb",label,data,&config->ffb))return true; + if(TestForInt("arcade",label,data,&config->arcade))return true; + if(TestForInt("reverse",label,data,&config->reverse))return true; + if(TestForInt("color32Bit",label,data,&config->color32Bit))return true; + if(TestForInt("metricUnits",label,data,&config->metricUnits))return true; + if(TestForInt("interiorDisplay",label,data,&config->interiorDisplay))return true; + if(TestForInt("cameraMode",label,data,&config->cameraMode))return true; + if(TestForInt("carsOnSpeed",label,data,&config->carsOnSpeed))return true; + if(TestForInt("demolition",label,data,&config->demolition))return true; + if(TestForInt("guideSigns",label,data,&config->guideSigns))return true; + if(TestForInt("trackerEnable",label,data,&config->trackerEnable))return true; + if(TestForInt("maxPlayers",label,data,&config->maxPlayers))return true; + if(TestForInt("registerLapTimes",label,data,&config->registerLapTimes))return true; + if(TestForInt("showPlayerNames",label,data,&config->showPlayerNames))return true; + if(TestForInt("motionBlur",label,data,&config->motionBlur))return true; + if(TestForHex("challengeData",label,data,&config->challengeData))return true; + if(TestForInt("dbIndex",label,data,&config->dbIndex))return true; + if(TestForInt("showReplays",label,data,&config->showReplays))return true; + if(TestForInt("allowHugeGames",label,data,&config->allowHugeGames))return true; + if(TestForInt("interfaceSounds",label,data,&config->interfaceSounds))return true; + if(TestForFloat("gfxDynamics",label,data,&config->gfxDynamics))return true; + if(TestForInt("seperateGasBrake",label,data,&config->seperateGasBrake))return true; + if(TestForInt("disableAnalogueTCS",label,data,&config->disableAnalogueTCS))return true; + if(TestForInt("reverseGas",label,data,&config->reverseGas))return true; + + if(TestForFile("lastCar",label,data,&config->lastCar))return true; + if(TestForFile("lastEnemy",label,data,&config->lastEnemy))return true; + if(TestForFile("lastRoad",label,data,&config->lastRoad))return true; + if(TestForInt("numEnemies",label,data,&config->numEnemies))return true; + if(TestForInt("automatic",label,data,&config->automatic))return true; + if(TestForInt("lastLaps",label,data,&config->lastLaps))return true; + if(TestForInt("lastColor",label,data,&config->lastColor))return true; + if(TestForFile("lastEnv",label,data,&config->lastEnv))return true; + + if(TestForString("playerName",label,data,config->playerName))return true; + if(TestForString("gameName",label,data,config->gameName))return true; + if(TestForString("password",label,data,config->password))return true; + if(TestForString("confirmedVersion",label,data,config->confirmedVersion))return true; + + if(TestForFloat("hudTransparency",label,data,&config->hudTransparency))return true; + if(TestForFloat("soundVolume",label,data,&config->soundVolume))return true; + if(TestForFloat("musicVolume",label,data,&config->musicVolume))return true; +// if(TestForFloat("clipFarDistance",label,data,&config->clipFarDistance))return true; + if(TestForFloat("ffbIntensity",label,data,&config->ffbIntensity))return true; + + if(TestForInt("numKeys",label,data,&config->numKeys)){ + config->keys=(tKeyConfig*)MemoryAllocateZeroedBlock(kInputNumControls*sizeof(tKeyConfig)); + if(config->numKeys!=kInputNumControls) + { + config->numKeys=kInputNumControls; + config->keys[kInputLeftIndicator].keyID=-1; + config->keys[kInputRightIndicator].keyID=-1; + } + return true; + } + + if(TestForHex("keyID",label,data,&config->keys[arrayPos].keyID))return true; + if(TestForHex("controllerID1",label,data,&config->keys[arrayPos].controllerID1))return true; + if(TestForHex("controllerID2",label,data,&config->keys[arrayPos].controllerID2))return true; + if(TestForHex("elementID",label,data,&config->keys[arrayPos].elementID))return true; + if(TestForString("identifier",label,data,config->keys[arrayPos].identifier))return true; + if(TestForString("controllerIdentifier",label,data,config->keys[arrayPos].controllerIdentifier))return true; + + if(TestForInt("numAxis",label,data,&config->numAxis)){ + config->numAxis=kInputNumAxis; + config->axis=(tAxisConfig*)MemoryAllocateZeroedBlock(config->numAxis*sizeof(tAxisConfig)); + return true; + } + if(TestForHex("axisControllerID1",label,data,&config->axis[arrayPos].axisControllerID1))return true; + if(TestForHex("axisControllerID2",label,data,&config->axis[arrayPos].axisControllerID2))return true; + if(TestForHex("axisElementID",label,data,&config->axis[arrayPos].axisElementID))return true; + if(TestForString("axisIdentifier",label,data,config->axis[arrayPos].axisIdentifier))return true; + if(TestForInt("min",label,data,&config->axis[arrayPos].min))return true; + if(TestForInt("mid",label,data,&config->axis[arrayPos].mid))return true; + if(TestForInt("max",label,data,&config->axis[arrayPos].max))return true; + if(TestForFloat("deadzone",label,data,&config->axis[arrayPos].deadzone))return true; + + if(TestForInt("numTaunts",label,data,&config->numTaunts)){ + config->taunts=(char(*)[256])MemoryAllocateZeroedBlock(config->numTaunts*sizeof(char)*256); + return true; + } + if(TestForString("taunts",label,data,config->taunts[arrayPos]))return true; + if(TestForFile("opponentCars",label,data,&config->opponentCars[arrayPos]))return true; + if(TestForFile("opponentColors",label,data,&config->opponentColors[arrayPos]))return true; + + if(TestForInt("challengeRecords",label,data,&config->challengeRecords[arrayPos]))return true; + + if(TestForInt("numPersonalRecords",label,data,&config->numPersonalRecords))return true; + if(TestForFile("records.car",label,data,&config->records[arrayPos].car))return true; + if(TestForFile("records.map",label,data,&config->records[arrayPos].map))return true; + if(TestForInt("records.mode",label,data,&config->records[arrayPos].mode))return true; + if(TestForInt("records.direction",label,data,&config->records[arrayPos].direction))return true; + if(TestForHex("records.time",label,data,&config->records[arrayPos].time))return true; + + return false; +} + +int ParseFontInfoDescLine(char *label,char *data,tFontInfo *font,int arrayPos) +{ + if(TestForFile("fontTexture",label,data,&font->fontTexture))return true; + if(TestForFloat("ascent",label,data,&font->ascent))return true; + if(TestForInt("startChar",label,data,&font->startChar))return true; + if(TestForInt("numChars",label,data,&font->numChars)){ + font->chars=(tFontCharInfo*)MemoryAllocateZeroedBlock(font->numChars*sizeof(tFontCharInfo)); + return true; + } + + if(TestForFloat("x1",label,data,&font->chars[arrayPos].x1))return true; + if(TestForFloat("x2",label,data,&font->chars[arrayPos].x2))return true; + if(TestForFloat("y1",label,data,&font->chars[arrayPos].y1))return true; + if(TestForFloat("y2",label,data,&font->chars[arrayPos].y2))return true; + return false; +} + +int ParseImageURLDescLine(char *label,char *data,tImageURL *url) +{ + if(TestForString("url",label,data,url->url))return true; + if(TestForFile("offlineImage",label,data,&url->offlineImage))return true; + return false; +} + +int ParseTexturePriorityDescLine(char *label,char *data,tTexturePriorityList *prior,int arrayPos) +{ + if(TestForInt("numPriorities",label,data,&prior->numPriorities)){ + prior->priorities=(tTexturePriority*)MemoryAllocateZeroedBlock(prior->numPriorities*sizeof(tTexturePriority)); + return true; + } + if(TestForFile("texture",label,data,&prior->priorities[arrayPos].texture))return true; + if(TestForInt("priority",label,data,&prior->priorities[arrayPos].priority))return true; + return false; +} + +int ParseSolidEntityDescLine(char *label,char *data,tSolidEntityPhysics *ent,int arrayPos) +{ + if(TestForFile("model",label,data,&ent->model))return true; + if(TestForFile("shadowModel",label,data,&ent->shadowModel))return true; + if(TestForFile("particle",label,data,&ent->particle))return true; + if(TestForFile("sound",label,data,&ent->sound))return true; + + if(TestForFloat("mass",label,data,&ent->mass))return true; + if(TestForFloat("inertia",label,data,&ent->inertia))return true; + + if(TestForInt("movable",label,data,&ent->movable))return true; + if(TestForInt("sparks",label,data,&ent->sparks))return true; + if(TestForInt("liquid",label,data,&ent->liquid))return true; + if(TestForInt("particleStick",label,data,&ent->particleStick))return true; + if(TestForInt("followPath",label,data,&ent->followPath))return true; + if(TestForInt("pathType",label,data,&ent->pathType))return true; + if(TestForInt("numColors",label,data,&ent->numColors))return true; + if(TestForInt("randomColor",label,data,&ent->randomColor))return true; + if(TestForInt("numSounds",label,data,&ent->numSounds))return true; + + if(TestForFloat("pathSize",label,data,&ent->pathSize))return true; + if(TestForFloat("pathVelo",label,data,&ent->pathVelo))return true; + if(TestForFloat("particleSize",label,data,&ent->particleSize))return true; + if(TestForFloat("particleAmount",label,data,&ent->particleAmount))return true; + if(TestForVector3("massCenter",label,data,&ent->massCenter))return true; + + if(TestForInt("numCollBoxes",label,data,&ent->numCollBoxes)){ + ent->coll=(tCollBox*)MemoryAllocateZeroedBlock(ent->numCollBoxes*sizeof(tCollBox)); + ent->maxCollRadius=0; + return true; + } + + if(TestForVector3("coll.tfr",label,data,&ent->coll[arrayPos].tfr))return true; + if(TestForVector3("coll.tfl",label,data,&ent->coll[arrayPos].tfl))return true; + if(TestForVector3("coll.trr",label,data,&ent->coll[arrayPos].trr))return true; + if(TestForVector3("coll.trl",label,data,&ent->coll[arrayPos].trl))return true; + if(TestForVector3("coll.bfr",label,data,&ent->coll[arrayPos].bfr))return true; + if(TestForVector3("coll.bfl",label,data,&ent->coll[arrayPos].bfl))return true; + if(TestForVector3("coll.brr",label,data,&ent->coll[arrayPos].brr))return true; + if(TestForVector3("coll.brl",label,data,&ent->coll[arrayPos].brl))return true; + + if(TestForInt("numLights",label,data,&ent->numLights)){ + ent->lights=(tLightDefinition*)MemoryAllocateZeroedBlock(ent->numLights*sizeof(tLightDefinition)); + return true; + } + + if(TestForVector3("lights.pos",label,data,&ent->lights[arrayPos].pos))return true; + if(TestForVector3("lights.dir",label,data,&ent->lights[arrayPos].dir)){ent->lights[arrayPos].dir=!ent->lights[arrayPos].dir;return true;} + if(TestForVector3("lights.rgb",label,data,&ent->lights[arrayPos].rgb))return true; + if(TestForInt("lights.type",label,data,&ent->lights[arrayPos].type))return true; + if(TestForFloat("lights.size",label,data,&ent->lights[arrayPos].size))return true; + if(TestForFloat("lights.radius",label,data,&ent->lights[arrayPos].radius))return true; + if(TestForHex("lights.onFlags",label,data,&ent->lights[arrayPos].onFlags))return true; + if(TestForHex("lights.offFlags",label,data,&ent->lights[arrayPos].offFlags))return true; + + return false; +} + +int ParseMapInfoDescLine(char *label,char *data,tMapInfo *mapInfo,int arrayPos) +{ + if(TestForFile("road",label,data,&mapInfo->road))return true; + if(TestForFile("image",label,data,&mapInfo->image))return true; + if(TestForVector3("startPos",label,data,&mapInfo->startPos))return true; + if(TestForVector3("finishPos",label,data,&mapInfo->finishPos))return true; + + if(TestForInt("builtIn",label,data,&mapInfo->builtIn))return true; + if(TestForInt("demoAvailable",label,data,&mapInfo->builtIn))return true; + if(TestForInt("useEnts",label,data,&mapInfo->demoAvailable))return true; + if(TestForInt("hideMap",label,data,&mapInfo->hideMap))return true; + if(TestForInt("dontDrawRoad",label,data,&mapInfo->dontDrawRoad))return true; + if(TestForInt("loop",label,data,&mapInfo->loop))return true; + if(TestForInt("reverse",label,data,&mapInfo->reverse))return true; + if(TestForInt("needsToStop",label,data,&mapInfo->needsToStop))return true; + if(TestForInt("dirtEnable",label,data,&mapInfo->dirtEnable))return true; + if(TestForInt("maxPlayers",label,data,&mapInfo->maxPlayers))return true; + if(TestForInt("playerPos",label,data,&mapInfo->playerPos))return true; + if(TestForInt("hasOverview",label,data,&mapInfo->hasOverview))return true; + if(TestForInt("baseSurfaceType",label,data,&mapInfo->baseSurfaceType))return true; + if(TestForInt("useAltEnv",label,data,&mapInfo->useAltEnv))return true; + + if(TestForVector2("overviewTopLeft",label,data,&mapInfo->overviewTopLeft))return true; + if(TestForVector2("overviewBotRight",label,data,&mapInfo->overviewBotRight))return true; + + if(TestForFloat("startLineOffset",label,data,&mapInfo->startLineOffset))return true; + if(TestForFloat("startCenterOffset",label,data,&mapInfo->startCenterOffset))return true; + if(TestForFloat("carOffset",label,data,&mapInfo->carOffset))return true; + + if(TestForFloat("speedFactor",label,data,&mapInfo->speedFactor))return true; + if(TestForFloat("dirtIntensity",label,data,&mapInfo->dirtIntensity))return true; + + if(TestForFile("roadTypes",label,data,&mapInfo->roadTypes))return true; + if(TestForFile("dirtMap",label,data,&mapInfo->dirtMap))return true; + if(TestForFile("overview",label,data,&mapInfo->overview))return true; + + if(TestForString("name",label,data,mapInfo->name))return true; + + if(TestForInt("numObjs",label,data,&mapInfo->numObjs)){ + mapInfo->obj=(tMapObjectDef*)MemoryAllocateZeroedBlock(mapInfo->numObjs*sizeof(tMapObjectDef)); + return true; + } + + if(TestForInt("numVisWalls",label,data,&mapInfo->numVisWalls)){ + mapInfo->visWalls=(tVisWall*)MemoryAllocateZeroedBlock(mapInfo->numVisWalls*sizeof(tVisWall)); + return true; + } + + if(TestForInt("numMapEnvs",label,data,&mapInfo->numMapEnvs)){ + mapInfo->mapEnv=(tMapEnv*)MemoryAllocateZeroedBlock(mapInfo->numMapEnvs*sizeof(tMapEnv)); + return true; + } + + if(TestForVector3("a",label,data,&mapInfo->visWalls[arrayPos].a))return true; + if(TestForVector3("b",label,data,&mapInfo->visWalls[arrayPos].b))return true; + if(TestForVector3("c",label,data,&mapInfo->visWalls[arrayPos].c))return true; + + if(TestForFile("obj.model",label,data,&mapInfo->obj[arrayPos].model))return true; + if(TestForVector3("obj.pos",label,data,&mapInfo->obj[arrayPos].pos))return true; + if(TestForVector3("obj.dir",label,data,&mapInfo->obj[arrayPos].dir))return true; + if(TestForInt("obj.color",label,data,&mapInfo->obj[arrayPos].color))return true; + if(TestForInt("obj.untouchable",label,data,&mapInfo->obj[arrayPos].untouchable))return true; + if(TestForHex("obj.envFlags",label,data,&mapInfo->obj[arrayPos].envFlags))return true; + + if(TestForHex("mapEnv.envFlags",label,data,&mapInfo->mapEnv[arrayPos].envFlags))return true; + if(TestForInt("mapEnv.lightEnable",label,data,&mapInfo->mapEnv[arrayPos].lightEnable))return true; + if(TestForFloat("mapEnv.fogBegin",label,data,&mapInfo->mapEnv[arrayPos].fogBegin))return true; + if(TestForFloat("mapEnv.fogEnd",label,data,&mapInfo->mapEnv[arrayPos].fogEnd))return true; + if(TestForFloat("mapEnv.fogOscillation",label,data,&mapInfo->mapEnv[arrayPos].fogOscillation))return true; + if(TestForFloat("mapEnv.fogOscillationSpeed",label,data,&mapInfo->mapEnv[arrayPos].fogOscillationSpeed))return true; + if(TestForVector3("mapEnv.fogColor",label,data,&mapInfo->mapEnv[arrayPos].fogColor))return true; + if(TestForFile("mapEnv.sky0",label,data,&mapInfo->mapEnv[arrayPos].sky0))return true; + if(TestForFile("mapEnv.sky90",label,data,&mapInfo->mapEnv[arrayPos].sky90))return true; + if(TestForFile("mapEnv.sky180",label,data,&mapInfo->mapEnv[arrayPos].sky180))return true; + if(TestForFile("mapEnv.sky270",label,data,&mapInfo->mapEnv[arrayPos].sky270))return true; + if(TestForFile("mapEnv.skytop",label,data,&mapInfo->mapEnv[arrayPos].skytop))return true; + if(TestForFile("mapEnv.skybot",label,data,&mapInfo->mapEnv[arrayPos].skybot))return true; + if(TestForFile("mapEnv.spheremap",label,data,&mapInfo->mapEnv[arrayPos].spheremap))return true; + if(TestForFile("mapEnv.lightMap",label,data,&mapInfo->mapEnv[arrayPos].lightMap))return true; + if(TestForVector2("mapEnv.lightMapTopLeft",label,data,&mapInfo->mapEnv[arrayPos].lightMapTopLeft))return true; + if(TestForVector2("mapEnv.lightMapBotRight",label,data,&mapInfo->mapEnv[arrayPos].lightMapBotRight))return true; + if(TestForVector2("mapEnv.lightMapSpeed",label,data,&mapInfo->mapEnv[arrayPos].lightMapSpeed))return true; + if(TestForFile("mapEnv.lightMap2",label,data,&mapInfo->mapEnv[arrayPos].lightMap2))return true; + if(TestForVector2("mapEnv.lightMap2TopLeft",label,data,&mapInfo->mapEnv[arrayPos].lightMap2TopLeft))return true; + if(TestForVector2("mapEnv.lightMap2BotRight",label,data,&mapInfo->mapEnv[arrayPos].lightMap2BotRight))return true; + if(TestForVector2("mapEnv.lightMap2Speed",label,data,&mapInfo->mapEnv[arrayPos].lightMap2Speed))return true; + if(TestForVector3("mapEnv.lightDir",label,data,&mapInfo->mapEnv[arrayPos].lightDir)){mapInfo->mapEnv[arrayPos].lightDir=!mapInfo->mapEnv[arrayPos].lightDir;return true;} + if(TestForVector3("mapEnv.flareDir",label,data,&mapInfo->mapEnv[arrayPos].flareDir)){mapInfo->mapEnv[arrayPos].flareDir=!mapInfo->mapEnv[arrayPos].flareDir;return true;} + + return false; +} + +int ParseGaugesListDescLine(char *label,char *data,tGaugesList *gauges,int arrayPos) +{ + if(TestForInt("numGauges",label,data,&gauges->numGauges)){ + gauges->gauges=(tGauge*)MemoryAllocateZeroedBlock(gauges->numGauges*sizeof(tGauge)); + return true; + } + + if(TestForFile("gaugeTexture",label,data,&gauges->gauges[arrayPos].gaugeTexture))return true; + if(TestForFile("pointerTexture",label,data,&gauges->gauges[arrayPos].pointerTexture))return true; + if(TestForFloat("pointerWidth",label,data,&gauges->gauges[arrayPos].pointerWidth))return true; + if(TestForFloat("gaugeZero",label,data,&gauges->gauges[arrayPos].gaugeZero))return true; + if(TestForFloat("gaugeMax",label,data,&gauges->gauges[arrayPos].gaugeMax))return true; + if(TestForInt("type",label,data,&gauges->gauges[arrayPos].type))return true; + + return false; +} + +int ParseChallengeListDescLine(char *label,char *data,tChallengeList *challenges,int arrayPos) +{ + if(TestForInt("numChallenges",label,data,&challenges->numChallenges)){ + challenges->challenges=(tChallenge*)MemoryAllocateZeroedBlock(challenges->numChallenges*sizeof(tChallenge)); + return true; + } + + if(TestForFile("map",label,data,&challenges->challenges[arrayPos].map))return true; + if(TestForFile("car",label,data,&challenges->challenges[arrayPos].car))return true; + if(TestForFile("environment",label,data,&challenges->challenges[arrayPos].environment))return true; + if(TestForFile("replay",label,data,&challenges->challenges[arrayPos].replay))return true; + if(TestForInt("color",label,data,&challenges->challenges[arrayPos].color))return true; + if(TestForInt("reverse",label,data,&challenges->challenges[arrayPos].reverse))return true; + if(TestForFloat("goldTime",label,data,&challenges->challenges[arrayPos].goldTime))return true; + if(TestForFloat("silverTime",label,data,&challenges->challenges[arrayPos].silverTime))return true; + if(TestForFloat("bronzeTime",label,data,&challenges->challenges[arrayPos].bronzeTime))return true; + if(TestForHex("requirements",label,data,&challenges->challenges[arrayPos].requirements))return true; + if(TestForString("name",label,data,challenges->challenges[arrayPos].name))return true; + if(TestForString("description",label,data,challenges->challenges[arrayPos].description))return true; + + return false; +} + +int ParseCarDescLine(char *label,char *data,tCarDefinition *car,int arrayPos,int arrayPos2) +{ + if(TestForFloat("frontAirResistance",label,data,&car->frontAirResistance))return true; + if(TestForFloat("sideAirResistance",label,data,&car->sideAirResistance))return true; + if(TestForFloat("topAirResistance",label,data,&car->topAirResistance))return true; + if(TestForFloat("frontLift",label,data,&car->frontLift))return true; + if(TestForFloat("rearLift",label,data,&car->rearLift))return true; + + if(TestForFloat("mass",label,data,&car->mass))return true; + if(TestForFloat("power",label,data,&car->power))return true; + if(TestForFloat("torque",label,data,&car->torque))return true; + if(TestForFloat("powerRPM",label,data,&car->powerRPM))return true; + if(TestForFloat("torqueRPM",label,data,&car->torqueRPM))return true; + if(TestForFloat("idleRPM",label,data,&car->idleRPM))return true; + if(TestForFloat("jerkRPM",label,data,&car->jerkRPM))return true; + + if(TestForFloat("clutchRPM",label,data,&car->clutchRPM))return true; + if(TestForFloat("maxRPM",label,data,&car->maxRPM))return true; + if(TestForFloat("shiftUpRPM",label,data,&car->shiftUpRPM))return true; + if(TestForFloat("shiftUpRPMFix",label,data,&car->shiftUpRPMFix))return true; + if(TestForFloat("shiftDownRPM",label,data,&car->shiftDownRPM))return true; + if(TestForFloat("engineInertia",label,data,&car->engineInertia))return true; + if(TestForFloat("engineFriction",label,data,&car->engineFriction))return true; + if(TestForFloat("engineBaseFriction",label,data,&car->engineBaseFriction))return true; + if(TestForFloat("engineRPMFriction",label,data,&car->engineRPMFriction))return true; + if(TestForFloat("finalDriveRatio",label,data,&car->finalDriveRatio))return true; + if(TestForFloat("differentialLockCoefficient",label,data,&car->differentialLockCoefficient))return true; + if(TestForFloat("maxClutchTorqueTransfer",label,data,&car->maxClutchTorqueTransfer))return true; + if(TestForFloat("aiSpeedIndex",label,data,&car->aiSpeedIndex))return true; + + if(TestForFloat("zeroRPMSoundGain",label,data,&car->zeroRPMSoundGain))return true; + if(TestForFloat("fullRPMSoundGain",label,data,&car->fullRPMSoundGain))return true; + if(TestForFloat("zeroThrottleSoundGain",label,data,&car->zeroThrottleSoundGain))return true; + if(TestForFloat("fullThrottleSoundGain",label,data,&car->fullThrottleSoundGain))return true; + + if(TestForFloat("zeroRPMSoundPitch",label,data,&car->zeroRPMSoundPitch))return true; + if(TestForFloat("fullRPMSoundPitch",label,data,&car->fullRPMSoundPitch))return true; + if(TestForFloat("zeroThrottleSoundPitch",label,data,&car->zeroThrottleSoundPitch))return true; + if(TestForFloat("fullThrottleSoundPitch",label,data,&car->fullThrottleSoundPitch))return true; + + if(TestForFloat("turboGain",label,data,&car->turboGain))return true; + + if(TestForVector3("steeringWheelPos",label,data,&car->steeringWheelPos))return true; + if(TestForVector3("driverPos",label,data,&car->driverPos))return true; + if(TestForFloat("steeringWheelAngle",label,data,&car->steeringWheelAngle))return true; + if(TestForFloat("steeringWheelTurns",label,data,&car->steeringWheelTurns))return true; + if(TestForFloat("steeringWheelRadius",label,data,&car->steeringWheelRadius))return true; + if(TestForFile("steeringWheelTexture",label,data,&car->steeringWheelTexture))return true; + if(TestForInt("noDriverModel",label,data,&car->noDriverModel))return true; + + if(TestForVector3("exhaust1Pos",label,data,&car->exhaust1Pos))return true; + if(TestForVector3("exhaust2Pos",label,data,&car->exhaust2Pos))return true; + if(TestForFloat("exhaustFire",label,data,&car->exhaustFire))return true; + + if(TestForVector3("frontLicensePlatePos",label,data,&car->frontLicensePlatePos))return true; + if(TestForVector3("rearLicensePlatePos",label,data,&car->rearLicensePlatePos))return true; + + if(TestForFloat("gearSwitchTime",label,data,&car->gearSwitchTime))return true; + + if(TestForFloat("supsensionFriction",label,data,&car->supsensionFriction))return true; + if(TestForFloat("damperStrength",label,data,&car->damperStrength))return true; + + if(TestForFloat("frontSwayBar",label,data,&car->frontSwayBar))return true; + if(TestForFloat("rearSwayBar",label,data,&car->rearSwayBar))return true; + + if(TestForInt("suspensionType",label,data,&car->demoAvailable))return true; + if(TestForInt("builtIn",label,data,&car->builtIn))return true; + if(TestForInt("demoAvailable",label,data,&car->builtIn))return true; + if(TestForInt("magic",label,data,&car->magic))return true; + if(TestForInt("secret",label,data,&car->secret))return true; + if(TestForHex("initialAddOns",label,data,&car->initialAddOns))return true; + if(TestForHex("challengeRequirements",label,data,&car->challengeRequirements))return true; + + if(TestForInt("numColors",label,data,&car->numColors)){ + car->colors=(tVector3*)MemoryAllocateZeroedBlock((car->numColors)*sizeof(tVector3)); + car->colorLoaded=(int*)MemoryAllocateZeroedBlock((car->numColors)*sizeof(int)); + return true; + } + if(TestForInt("numGears",label,data,&car->numGears)){ + car->gearRatios=(float*)MemoryAllocateZeroedBlock((car->numGears)*sizeof(float)); + return true; + } + if(TestForInt("numWheels",label,data,&car->numWheels)){ + car->wheels=(tWheelDefinition*)MemoryAllocateZeroedBlock(car->numWheels*sizeof(tWheelDefinition)); + return true; + } + if(TestForInt("numLights",label,data,&car->numLights)){ + car->lights=(tLightDefinition*)MemoryAllocateZeroedBlock(car->numLights*sizeof(tLightDefinition)); + return true; + } + if(TestForInt("numAddOns",label,data,&car->numAddOns)){ + car->addOns=(tAddOnDefinition*)MemoryAllocateZeroedBlock((car->numAddOns)*sizeof(tAddOnDefinition)); + return true; + } + if(TestForInt("numCollBoxes",label,data,&car->numCollBoxes)){ + car->coll=(tCollBox*)MemoryAllocateZeroedBlock(car->numCollBoxes*sizeof(tCollBox)); + car->maxCollRadius=0; + return true; + } + + if(TestForVector3("colors",label,data,&car->colors[arrayPos]))return true; + + if(TestForString("carName",label,data,car->carName))return true; + if(TestForFloat("displacement",label,data,&car->displacement))return true; + if(TestForInt("year",label,data,&car->year))return true; + if(TestForInt("price",label,data,&car->price))return true; + + if(TestForVector3("coll.tfr",label,data,&car->coll[arrayPos].tfr))return true; + if(TestForVector3("coll.tfl",label,data,&car->coll[arrayPos].tfl))return true; + if(TestForVector3("coll.trr",label,data,&car->coll[arrayPos].trr))return true; + if(TestForVector3("coll.trl",label,data,&car->coll[arrayPos].trl))return true; + if(TestForVector3("coll.bfr",label,data,&car->coll[arrayPos].bfr))return true; + if(TestForVector3("coll.bfl",label,data,&car->coll[arrayPos].bfl))return true; + if(TestForVector3("coll.brr",label,data,&car->coll[arrayPos].brr))return true; + if(TestForVector3("coll.brl",label,data,&car->coll[arrayPos].brl))return true; + + + if(TestForVector3("massCenter",label,data,&car->massCenter))return true; + if(TestForVector3("inertia",label,data,&car->inertia))return true; + if(TestForFile("model",label,data,&car->model))return true; + if(TestForFile("shadowModel",label,data,&car->shadowModel))return true; + if(TestForFile("interiorModel",label,data,&car->interiorModel))return true; + if(TestForFile("engineSample",label,data,&car->engineSample))return true; + if(TestForFile("hallSample",label,data,&car->hallSample))return true; + if(TestForFile("turboSample",label,data,&car->turboSample))return true; + if(TestForFile("hornSample",label,data,&car->hornSample))return true; + if(TestForFloat("hornPitch",label,data,&car->hornPitch))return true; + + if(TestForFloat("gearRatios",label,data,&car->gearRatios[arrayPos]))return true; + + if(TestForVector3("wheels.pos",label,data,&car->wheels[arrayPos].pos))return true; + if(TestForFloat("wheels.powered",label,data,&car->wheels[arrayPos].powered))return true; + if(TestForFloat("wheels.inertia",label,data,&car->wheels[arrayPos].inertia))return true; + if(TestForFloat("wheels.friction",label,data,&car->wheels[arrayPos].friction))return true; + if(TestForFloat("wheels.maxAngle",label,data,&car->wheels[arrayPos].maxAngle))return true; + if(TestForFloat("wheels.maxSuspension",label,data,&car->wheels[arrayPos].maxSuspension))return true; + if(TestForFloat("wheels.radius",label,data,&car->wheels[arrayPos].radius))return true; + if(TestForFloat("wheels.width",label,data,&car->wheels[arrayPos].width))return true; + if(TestForFloat("wheels.rollCenter",label,data,&car->wheels[arrayPos].rollCenter))return true; + if(TestForFloat("wheels.grip",label,data,&car->wheels[arrayPos].grip))return true; + if(TestForFloat("wheels.braked",label,data,&car->wheels[arrayPos].braked))return true; + if(TestForFloat("wheels.handbraked",label,data,&car->wheels[arrayPos].handbraked))return true; + if(TestForFloat("wheels.tolerance",label,data,&car->wheels[arrayPos].tolerance))return true; + if(TestForFloat("wheels.tilt",label,data,&car->wheels[arrayPos].tilt))return true; + if(TestForFloat("wheels.loadSensitivity",label,data,&car->wheels[arrayPos].loadSensitivity))return true; + if(TestForFloat("wheels.stickyness",label,data,&car->wheels[arrayPos].stickyness))return true; + if(TestForInt("wheels.texture",label,data,&car->wheels[arrayPos].texture))return true; + if(TestForInt("wheels.noShadow",label,data,&car->wheels[arrayPos].noShadow))return true; + if(TestForFile("wheels.model",label,data,&car->wheels[arrayPos].model))return true; + if(TestForFile("wheels.customBrakeModel",label,data,&car->wheels[arrayPos].customBrakeModel))return true; + + if(TestForVector3("lights.pos",label,data,&car->lights[arrayPos].pos))return true; + if(TestForVector3("lights.dir",label,data,&car->lights[arrayPos].dir)){car->lights[arrayPos].dir=!car->lights[arrayPos].dir;return true;} + if(TestForVector3("lights.rgb",label,data,&car->lights[arrayPos].rgb))return true; + if(TestForInt("lights.type",label,data,&car->lights[arrayPos].type))return true; + if(TestForFloat("lights.size",label,data,&car->lights[arrayPos].size))return true; + if(TestForFloat("lights.radius",label,data,&car->lights[arrayPos].radius))return true; + if(TestForHex("lights.onFlags",label,data,&car->lights[arrayPos].onFlags))return true; + if(TestForHex("lights.offFlags",label,data,&car->lights[arrayPos].offFlags))return true; + + if(TestForHex("addOns.requirements",label,data,&car->addOns[arrayPos].requirements))return true; + if(TestForHex("addOns.conflicts",label,data,&car->addOns[arrayPos].conflicts))return true; + if(TestForFloat("addOns.mass",label,data,&car->addOns[arrayPos].mass))return true; + if(TestForFloat("addOns.frontLift",label,data,&car->addOns[arrayPos].frontLift))return true; + if(TestForFloat("addOns.rearLift",label,data,&car->addOns[arrayPos].rearLift))return true; + if(TestForFloat("addOns.frontSwayBar",label,data,&car->addOns[arrayPos].frontSwayBar))return true; + if(TestForFloat("addOns.rearSwayBar",label,data,&car->addOns[arrayPos].rearSwayBar))return true; + if(TestForFloat("addOns.track",label,data,&car->addOns[arrayPos].track))return true; + if(TestForFloat("addOns.engineInertia",label,data,&car->addOns[arrayPos].engineInertia))return true; + if(TestForFloat("addOns.topGearRatio",label,data,&car->addOns[arrayPos].topGearRatio))return true; + if(TestForFloat("addOns.damperStrength",label,data,&car->addOns[arrayPos].damperStrength))return true; + if(TestForFloat("addOns.exhaustFire",label,data,&car->addOns[arrayPos].exhaustFire))return true; + if(TestForFloat("addOns.frontMaxSuspension",label,data,&car->addOns[arrayPos].frontMaxSuspension))return true; + if(TestForFloat("addOns.rearMaxSuspension",label,data,&car->addOns[arrayPos].rearMaxSuspension))return true; + if(TestForFloat("addOns.frontWheelWidth",label,data,&car->addOns[arrayPos].frontWheelWidth))return true; + if(TestForFloat("addOns.rearWheelWidth",label,data,&car->addOns[arrayPos].rearWheelWidth))return true; + if(TestForFloat("addOns.frontAirResistance",label,data,&car->addOns[arrayPos].frontAirResistance))return true; + if(TestForFloat("addOns.differentialLockCoefficient",label,data,&car->addOns[arrayPos].differentialLockCoefficient))return true; + if(TestForFloat("addOns.finalDriveRatio",label,data,&car->addOns[arrayPos].finalDriveRatio))return true; + if(TestForFloat("addOns.gearSwitchTime",label,data,&car->addOns[arrayPos].gearSwitchTime))return true; + if(TestForFloat("addOns.powerPercent",label,data,&car->addOns[arrayPos].powerPercent))return true; + if(TestForFloat("addOns.torquePercent",label,data,&car->addOns[arrayPos].torquePercent))return true; + if(TestForFloat("addOns.maxRPM",label,data,&car->addOns[arrayPos].maxRPM))return true; + if(TestForVector3("addOns.massCenter",label,data,&car->addOns[arrayPos].massCenter))return true; + if(TestForInt("addOns.hasGraphic",label,data,&car->addOns[arrayPos].hasGraphic))return true; + if(TestForInt("addOns.price",label,data,&car->addOns[arrayPos].price))return true; + if(TestForInt("addOns.group",label,data,&car->addOns[arrayPos].group))return true; + if(TestForFile("addOns.model",label,data,&car->addOns[arrayPos].model))return true; + if(TestForString("addOns.name",label,data,car->addOns[arrayPos].name))return true; + if(TestForString("addOns.describtion",label,data,car->addOns[arrayPos].describtion))return true; + + return false; +} + + +//Stores the next line of the character buffer at *filePos in the string buffer +//at line, and then updates *filePos to point to the start of the next line. +// +//*fileEnd: a pointer to the end of the characted buffer at *filePos. +//buffersize: the size of the string buffer at line. +void GetLine(char *line,char **filePos,char *fileEnd,int buffersize) +{ + char *lineSeek=*filePos; + while(*lineSeek!='\n'&&*lineSeek!='\r'&&lineSeekdemoAvailable=false; + if(fileType==kParserTypeCarDesc) + if(fileId!=FileGetReference("corvette.car")) + if(fileId!=FileGetReference("dmc12.car")) + if(fileId!=FileGetReference("gti.car")) + if(fileId!=FileGetReference("mini.car")) + ((tCarDefinition*)dataBuffer)->demoAvailable=false; + +} \ No newline at end of file diff --git a/source/parser.h b/source/parser.h new file mode 100755 index 0000000..eedbe89 --- /dev/null +++ b/source/parser.h @@ -0,0 +1 @@ +#ifndef __PARSER #define __PARSER #include "fileio.h" enum{ kParserTypeCarDesc, kParserTypeRoadTypeDesc, kParserTypeSurfaceTypeDesc, kParserTypeConfigDesc, kParserTypeEnvironmentDesc, kParserTypeMapInfoDesc, kParserTypeSolidEntityDesc, kParserTypeFontInfoDesc, kParserTypeCareerDataDesc, kParserTypeCareerRaceDataDesc, kParserTypeGaugesListDesc, kParserTypeChallengeList, kParserTypeTexturePriorityList, kParserTypeImageURL, }; int ParseFile(tFileRef fileId, void *dataBuffer,int fileType); void CryptData(char *data,int size); void GetLine(char *line,char **filePos,char *fileEnd,int buffersize); #endif \ No newline at end of file diff --git a/source/particles.cpp b/source/particles.cpp new file mode 100644 index 0000000..fb7d83c --- /dev/null +++ b/source/particles.cpp @@ -0,0 +1,484 @@ +//particles.cpp +//game's particle engine, for smoke, sparks, rain, etc.. + +#include +#include +#include "vectors.h" +#include "gamemem.h" +#include "random.h" +#include "renderframe.h" +#include "textures.h" +#include "particles.h" +#include "entities.h" +#include "gameframe.h" +#include "network.h" +#include "log.h" +#include "gameinitexit.h" +#include "error.h" +#include "environment.h" +#include "fileio.h" +#include "collision.h" +#include "roads.h" + +//a single particle +typedef struct{ + tVector3 pos,velo; + float life; //how much lifetime the particle has left (in seconds) +}tParticle; + +//an entry in the particle list +typedef struct{ + void *next; //next list member + int numParticles; //number of particles in this list entry + float life; //how much lifetime the list entry has left (in seconds) + tParticlesDef def; //type of particles + tParticle particles[1];//the actual particle data +}tParticleListEntry; + +//an entry in the screen particle list +//screen particles are particles on the 2d screen plane (such as raindrops on the camera) +//unlike the normal particle list, this only refers to a single particle +typedef struct{ + void *next; //next list member + float x1,x2,y1,y2; //coordinates of particle rectangle + int texture; //file id of texture to be used + float gravity; //how fast the particle runs down on the screen + float maxLife; //how much lifetime the particle had left when it had full alpha + float life; //how much lifetime the particle has left +} tScreenParticleListEntry; + + +tParticleListEntry *gParticleList=nil; //the particles list +tScreenParticleListEntry *gScreenParticleList=nil; //the screen particles list +int gNumScreenParticles=0; //number of screen particles used +float gCameraTunnel=0; +#define kMaxScreenParticles 25 //delimiter for screen particles (for performance reasons) +#define kSparkVeloScale 0.2 //how long velocity dependant partcles (like sparks) are per m/s of speed + + +//adds an entry to the particle list. quantity particles will be created +//(quantity is rounded based on random probilities, ie. 1.6 will be rounded +//to 2 with a probility of 60% and to 1 with a probility of 40%). +void ParticlesCreate(float quantity,tVector3 pos,tVector3 velo,tParticlesDef *def) +{ + //round quantity + float iQuan=floor(quantity); + int numParticles=iQuan+(RandomProb(quantity-iQuan)?1:0); + if(numParticles<=0) return; + + //allocate particleListEntry + tParticleListEntry *particleListEntry; + particleListEntry=(tParticleListEntry*)MemoryAllocateBlock(sizeof(tParticleListEntry)+sizeof(tParticle)*(numParticles-1)); + + //initialize particleListEntry + particleListEntry->next=gParticleList; + gParticleList=particleListEntry; + + particleListEntry->numParticles=numParticles; + particleListEntry->def=*def; + particleListEntry->life=def->maxLife; + + //initialize particles + for(int i=0;iparticles[i].pos=pos+Vector(RandomFl(-def->maxSpread,def->maxSpread),RandomFl(-def->maxSpread,def->maxSpread),RandomFl(-def->maxSpread,def->maxSpread)); + particleListEntry->particles[i].velo=Vector(RandomFl(-def->maxVelo,def->maxVelo),RandomFl(-def->maxVelo,def->maxVelo),RandomFl(-def->maxVelo,def->maxVelo))+velo; + particleListEntry->particles[i].life=RandomFl(0,def->maxLife); + } +} + + +//adds an entry to the screen particle list +void ScreenParticleCreate(tVector3 pos,float gravity,float size,int texture,float maxLife,float life) +{ + //are there still any screen particles free? + if(gNumScreenParticleslife=life; + particleListEntry->maxLife=maxLife; + particleListEntry->texture=texture; + particleListEntry->gravity=gravity; + + tVector3 center=((pos-gCameraEntity->pos)*gCameraEntity->dir)*0.5; + + particleListEntry->x1=center.x-size*0.5; + particleListEntry->x2=center.x+size*0.5; + particleListEntry->y1=center.y-size*0.5; + particleListEntry->y2=center.y+size*0.5; + + particleListEntry->next=gScreenParticleList; + gScreenParticleList=particleListEntry; + gNumScreenParticles++; + } +} + +void ParticlesInitDef(tParticlesDef *def) +{ + def->sprite=FileGetReference("null.raw"); + def->veloRotation=false; + def->screenTexture=-1; + def->grow=false; + def->brightness=1; + def->r=1; + def->b=1; + def->g=1; + def->a=1; + def->xSize=1; + def->ySize=1; + def->maxSpread=0; + def->maxVelo=0; + def->maxLife=1; + def->gravity=0; + def->screenGravity=0; + def->notInTunnel=false; + def->multi=1; +} + +//Draw the particle list +void DrawParticleList() +{ + tParticleListEntry *particleListEntry=gParticleList; + + //get the ground type under the camera + tVector3 groundNormal; + int surfaceType; + GetGroundOffset(gCameraEntity->pos,&gCameraEntity->lastRoadIndex,&groundNormal,&surfaceType); + //check if we are in a tunnel, there should be no rain in there. + if(surfaceType) + if(gSurfaceTypes->types[surfaceType].soundEcho) + gCameraTunnel+=10*kFrameTime; + else + gCameraTunnel-=10*kFrameTime; + if(gCameraTunnel>1)gCameraTunnel=1; + if(gCameraTunnel<0)gCameraTunnel=0; + + while(particleListEntry) + { + TexturesSelectTex(particleListEntry->def.sprite); + for(int i=0;inumParticles;i++) + //does the particle have life left? + if(particleListEntry->particles[i].life>0) + { + float bright=particleListEntry->def.brightness; + float a=1; + if(particleListEntry->def.notInTunnel) + a=1-gCameraTunnel; + //alpha blend according to life left + glColor4f(bright*particleListEntry->def.r,bright*particleListEntry->def.g,bright*particleListEntry->def.b,(particleListEntry->particles[i].life/particleListEntry->def.maxLife)*particleListEntry->def.a*a); + + if(!particleListEntry->def.veloRotation) + { + //translate to position of particle + SetupTranslation(particleListEntry->particles[i].pos,gCameraEntity->dir); + + //draw the particle + float xSize=particleListEntry->def.xSize*0.5*(particleListEntry->def.grow?1-(particleListEntry->particles[i].life/particleListEntry->def.maxLife):1); + float ySize=particleListEntry->def.ySize*0.5*(particleListEntry->def.grow?1-(particleListEntry->particles[i].life/particleListEntry->def.maxLife):1); + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex2f(xSize,ySize); + glTexCoord2d(1,0); glVertex2f(xSize,-ySize); + glTexCoord2d(0,1); glVertex2f(-xSize,ySize); + glTexCoord2d(0,0); glVertex2f(-xSize,-ySize); + glEnd(); + #ifdef __POLYCOUNT + gPolyCount+=2; + #endif + } + else //veloRotation: the particle must be rotated to point into the direction it is moving (eg.: sparks) + { + + //set up an translate to a matrix rotated to the direction a particle is moving + tMatrix3 dir; + tVector3 velo=particleListEntry->particles[i].velo-gCameraEntity->velo; + float fvelo=~particleListEntry->particles[i].velo; + *MatrixGetZVector(dir)=velo*1/fvelo; + *MatrixGetYVector(dir)=Vector(0,1,0); + MatrixReAdjust(dir); + fvelo*=kSparkVeloScale; + SetupTranslation(particleListEntry->particles[i].pos,dir); + for(int num=0;numdef.multi;num++) + { + glTranslatef(sin(num)*num,0,cos(num)*num); + //draw the particle as two crossed quads. + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,0); glVertex3f(particleListEntry->def.xSize*0.5,0,particleListEntry->def.ySize*0.5*fvelo); + glTexCoord2d(1,1); glVertex3f(particleListEntry->def.xSize*0.5,0,-particleListEntry->def.ySize*0.5*fvelo); + glTexCoord2d(0,0); glVertex3f(-particleListEntry->def.xSize*0.5,0,particleListEntry->def.ySize*0.5*fvelo); + glTexCoord2d(0,1); glVertex3f(-particleListEntry->def.xSize*0.5,0,-particleListEntry->def.ySize*0.5*fvelo); + glEnd(); + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,0); glVertex3f(0,particleListEntry->def.xSize*0.5,particleListEntry->def.ySize*0.5*fvelo); + glTexCoord2d(1,1); glVertex3f(0,particleListEntry->def.xSize*0.5,-particleListEntry->def.ySize*0.5*fvelo); + glTexCoord2d(0,0); glVertex3f(0,-particleListEntry->def.xSize*0.5,particleListEntry->def.ySize*0.5*fvelo); + glTexCoord2d(0,1); glVertex3f(0,-particleListEntry->def.xSize*0.5,-particleListEntry->def.ySize*0.5*fvelo); + glEnd(); + } + #ifdef __POLYCOUNT + gPolyCount+=4; + #endif + } + + + } + //jump to next list entry + particleListEntry=(tParticleListEntry*)particleListEntry->next; + } +} + +//draw screen particles +void DrawScreenParticleList() +{ + tScreenParticleListEntry *screenParticleListEntry=gScreenParticleList; + while(screenParticleListEntry) + { + TexturesSelectTex(screenParticleListEntry->texture); + glColor4f(1,1,1,(screenParticleListEntry->life/screenParticleListEntry->maxLife)); + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex3f(screenParticleListEntry->x1,screenParticleListEntry->y2,-1); + glTexCoord2d(1,0); glVertex3f(screenParticleListEntry->x1,screenParticleListEntry->y1,-1); + glTexCoord2d(0,1); glVertex3f(screenParticleListEntry->x2,screenParticleListEntry->y2,-1); + glTexCoord2d(0,0); glVertex3f(screenParticleListEntry->x2,screenParticleListEntry->y1,-1); + glEnd(); + screenParticleListEntry=(tScreenParticleListEntry*)screenParticleListEntry->next; + } +} + +//draw particles +void ParticlesDraw() +{ + glPushAttrib(GL_DEPTH_BUFFER_BIT+GL_CURRENT_BIT+GL_LIGHTING_BIT+GL_COLOR_BUFFER_BIT+GL_POLYGON_BIT); + glDepthMask(GL_FALSE); + glEnable(GL_BLEND); + glDisable(GL_LIGHTING); + glDisable(GL_CULL_FACE); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + glNormal3f(0,1,0); + glPushMatrix(); + + DrawParticleList(); + + glLoadIdentity(); + glDisable(GL_DEPTH_TEST); + + DrawScreenParticleList(); + + glPopMatrix(); + glPopAttrib(); +} + + + +#define kEnvironmentParticlesSpread 25 +#define kEnvironmentParticlesLife 1.5 + + +//create particles as defined by the environment (such as rain) +void CreateEnvironmentalParticles() +{ + //are particles enabled by the environment? + if(gEnvironment->particlesEnable) + { + //set up environment particles + tParticlesDef def; + def.sprite=gEnvironment->particlesType; + def.maxSpread=kEnvironmentParticlesSpread; + def.maxVelo=gEnvironment->particlesVeloSpread; + def.gravity=0; + def.multi=5; + def.screenTexture=-1; + def.brightness=1.0; + def.maxLife=gEnvironment->particlesLife; + def.grow=false; + def.xSize=gEnvironment->particlesSize.x;def.ySize=gEnvironment->particlesSize.y; + def.veloRotation=gEnvironment->particlesSize.x!=gEnvironment->particlesSize.y; + def.brightness=1; + def.notInTunnel=true; + def.r=1;def.g=1;def.b=1;def.a=10; + + //and add them to the particle list + float quantity=gEnvironment->particlesAmount*kFrameTime; + ParticlesCreate(quantity, + gCameraEntity->pos+gCameraEntity->velo+*MatrixGetZVector(gCameraEntity->dir)*kEnvironmentParticlesSpread+Vector(0,gEnvironment->particlesHeight,0) + ,gEnvironment->particlesVelo,&def); + + //go to the particle list entry just created, + //and set the life of all particles to kEnvironmentParticlesLife. + //we don't want raindrops to fade out before hitting the ground! + if(gParticleList) + for(int i=0;inumParticles;i++) + gParticleList->particles[i].life=gEnvironment->particlesLife; + + if(gEnvironment->screenParticlesEnable) + //occasionally add a new raindrop running down the screen. + if(RandomProb(gEnvironment->particlesAmount*kFrameTime*0.001*(1+sqr(gCameraEntity->velo)*0.003))) + { + tVector3 pos=gCameraEntity->pos+*MatrixGetZVector(gCameraEntity->dir)+Vector(RandomFl(-1,1),RandomFl(-1,1),RandomFl(-1,1)); + ScreenParticleCreate(pos,RandomFl(0.008,0.02),RandomFl(0.03,0.08),FileGetReference("raindrop-screen.pct"),RandomFl(0,1.5),1.5); + } + } +/* + if(gEnvironment->particlesEnable) + { + //set up environment particles + tParticlesDef def; + def.sprite=FileGetReference("snow.pct"); + def.maxSpread=kEnvironmentParticlesSpread; + def.maxVelo=1; + def.gravity=0; + def.multi=10; + def.screenTexture=-1; + def.brightness=1.0; + def.maxLife=15; + def.grow=false; + def.xSize=0.2;def.ySize=0.2; + def.veloRotation=false; + def.brightness=1; + def.notInTunnel=true; + def.r=1;def.g=1;def.b=1;def.a=10; + + //and add them to the particle list + float quantity=gEnvironment->particlesAmount*kFrameTime; + ParticlesCreate(quantity, + gCameraEntity->pos+gCameraEntity->velo+*MatrixGetZVector(gCameraEntity->dir)*kEnvironmentParticlesSpread+Vector(0,kEnvironmentParticlesSpread*0.2,0) + ,Vector(0,-2,0),&def); + + //go to the particle list entry just created, + //and set the life of all particles to kEnvironmentParticlesLife. + //we don't want raindrops to fade out before hitting the ground! + if(gParticleList) + for(int i=0;inumParticles;i++) + gParticleList->particles[i].life=kEnvironmentParticlesLife; + }*/ + +} + + +//runs through the particle list, moving all the particles as required. +void ProcessParticleList() +{ + tParticleListEntry *particleListEntry=gParticleList; + tParticleListEntry **lastEntry=&gParticleList; + + while(particleListEntry) + { + //decrement particle list entry life time + particleListEntry->life-=kFrameTime; + //if lifetime is <=0, remove entry. + if(particleListEntry->life<=0) + { + *lastEntry=(tParticleListEntry*)particleListEntry->next; + tParticleListEntry *next=(tParticleListEntry*)particleListEntry->next; + MemoryFreeBlock(particleListEntry); + particleListEntry=next; + } + else + { + for(int i=0;inumParticles;i++) + //if particle has life left + if(particleListEntry->particles[i].life>0) + { + //move particle + particleListEntry->particles[i].pos=particleListEntry->particles[i].pos+particleListEntry->particles[i].velo*kFrameTime; + + //gravitional accelerate particle + particleListEntry->particles[i].velo.y-=particleListEntry->def.gravity*kFrameTime; + + //decrement lifetiem + particleListEntry->particles[i].life-=kFrameTime; + + //can the particle leave traces on the screen? (such as water clouds) + if(particleListEntry->def.screenTexture!=-1&&gCameraMode!=kCameraCockpit) + //is particle within 1 meter of camera? + if(sqr(particleListEntry->particles[i].pos-gCameraEntity->pos)<1) + { + //create a new screen particle + ScreenParticleCreate(particleListEntry->particles[i].pos,particleListEntry->def.screenGravity,particleListEntry->def.xSize,particleListEntry->def.screenTexture,particleListEntry->life*2,particleListEntry->particles[i].life*2); + + //and kill the impacted particle + particleListEntry->particles[i].life=0; + } + } + + //jump to next particle. + lastEntry=(tParticleListEntry**)particleListEntry; + particleListEntry=(tParticleListEntry*)particleListEntry->next; + } + } + +} + + +//runs through the screen particle list, moving all the particles as required. +void ProcessScreenParticleList() +{ + tScreenParticleListEntry *screenParticleListEntry=gScreenParticleList; + tScreenParticleListEntry **screenLastEntry=&gScreenParticleList; + float gravFactor=1+sqr(gCameraEntity->velo)*0.015; + while(screenParticleListEntry) + { + //decrement life + screenParticleListEntry->life-=kFrameTime*0.05; + + //if lifetime is <=0, remove entry. + if(screenParticleListEntry->life<=0) + { + *screenLastEntry=(tScreenParticleListEntry*)screenParticleListEntry->next; + tScreenParticleListEntry *next=(tScreenParticleListEntry*)screenParticleListEntry->next; + MemoryFreeBlock(screenParticleListEntry); + screenParticleListEntry=next; + gNumScreenParticles--; + } + else + { + //move particle down the screen + screenParticleListEntry->y1-=screenParticleListEntry->gravity*kFrameTime*gravFactor; + screenParticleListEntry->y2-=screenParticleListEntry->gravity*kFrameTime*gravFactor; + + //jump to next particle + screenLastEntry=(tScreenParticleListEntry**)screenParticleListEntry; + screenParticleListEntry=(tScreenParticleListEntry*)screenParticleListEntry->next; + } + } +} + +//process all particle movement +void ParticlesProcess() +{ + CreateEnvironmentalParticles(); + ProcessParticleList(); + ProcessScreenParticleList(); +} + +void ParticlesClearScreen() +{ + tScreenParticleListEntry *screenParticleListEntry=gScreenParticleList; + tScreenParticleListEntry **screenLastEntry=&gScreenParticleList; + while(screenParticleListEntry) + { + *screenLastEntry=(tScreenParticleListEntry*)screenParticleListEntry->next; + tScreenParticleListEntry *next=(tScreenParticleListEntry*)screenParticleListEntry->next; + MemoryFreeBlock(screenParticleListEntry); + screenParticleListEntry=next; + gNumScreenParticles--; + } +} + +void ParticlesClear() +{ + ParticlesClearScreen(); + + tParticleListEntry *particleListEntry=gParticleList; + tParticleListEntry **lastEntry=&gParticleList; + + while(particleListEntry) + { + *lastEntry=(tParticleListEntry*)particleListEntry->next; + tParticleListEntry *next=(tParticleListEntry*)particleListEntry->next; + MemoryFreeBlock(particleListEntry); + particleListEntry=next; + } +} + diff --git a/source/particles.h b/source/particles.h new file mode 100644 index 0000000..88c8b71 --- /dev/null +++ b/source/particles.h @@ -0,0 +1 @@ +#ifndef __PARTICLES #define __PARTICLES typedef struct{ int sprite; int veloRotation; int screenTexture; int grow; int multi; int notInTunnel; float brightness; float r,g,b,a; float xSize,ySize; float maxSpread,maxVelo; float maxLife; float gravity,screenGravity; }tParticlesDef; void ParticlesCreate(float quantity,tVector3 pos,tVector3 velo,tParticlesDef *def); void ParticlesDraw(); void ParticlesProcess(); void ParticlesClearScreen(); void ParticlesClear(); void ParticlesInitDef(tParticlesDef *def); #endif \ No newline at end of file diff --git a/source/random.cpp b/source/random.cpp new file mode 100644 index 0000000..dc02744 --- /dev/null +++ b/source/random.cpp @@ -0,0 +1,87 @@ +//random.cpp +//random number generator + +#include +#include +#include +/* A C-program for TT800 : July 8th 1996 Version */ +/* by M. Matsumoto, email: matumoto@math.keio.ac.jp */ +/* genrand() generate one pseudorandom number with double precision */ +/* which is uniformly distributed on [0,1]-interval */ +/* for each call. One may choose any initial 25 seeds */ +/* except all zeros. */ + +/* See: ACM Transactions on Modelling and Computer Simulation, */ +/* Vol. 4, No. 3, 1994, pages 254-266. */ + +#define N 25 +#define M 7 + +unsigned long x[N]; /* initial 25 seeds, change as you wish */ + +double genrand() +{ + unsigned long y; + static int k = 0; + static unsigned long mag01[2]={ + 0x0, 0x8ebfd028 /* this is magic vector `a', don't change */ + }; + if (k==N) { /* generate N words at one time */ + int kk; + for (kk=0;kk> 1) ^ mag01[x[kk] % 2]; + } + for (; kk> 1) ^ mag01[x[kk] % 2]; + } + k=0; + } + y = x[k]; + y ^= (y << 7) & 0x2b5b2500; /* s and b, magic vectors */ + y ^= (y << 15) & 0xdb8b0000; /* t and c, magic vectors */ + y &= 0xffffffff; /* you may delete this line if word size = 32 */ +/* + the following line was added by Makoto Matsumoto in the 1996 version + to improve lower bit's corellation. + Delete this line to o use the code published in 1994. +*/ + y ^= (y >> 16); /* added to the 1994 version */ + k++; + return( (double) y / (unsigned long) 0xffffffff); +} + + +//APIs to use genrand function + + +void RandomInit() +//Inits the random seeds using the standard C rand() function +{ + int i; + time_t t; + time(&t); + srand(t); + for(i=0;i +#include +#include +#include "carphysics.h" +#include "vectors.h" +#include "models.h" +#include "entities.h" +#include "fileio.h" +#include "textures.h" +#include "renderframe.h" +#include "screen.h" +#include "collision.h" +#include "environment.h" +#include "text.h" +#include "config.h" +#include "gameinitexit.h" +#include "roads.h" +#include "rendercar.h" +#include "gameframe.h" +#include "config.h" +#include "transparency.h" +#include "stencil.h" +#include "random.h" + +#define sign(x) ((x)<0?-1:1) + + +#define kCarMinShadow 3 + +void RenderGauge(tGauge *gauge,float value,float xPos,float yPos,float size,float opacity,float redline) +{ + gTexturesQualityModifier=-255; + glPushMatrix(); + + glPushAttrib(GL_DEPTH_BUFFER_BIT+GL_LIGHTING_BIT+GL_COLOR_BUFFER_BIT+GL_TEXTURE_BIT+GL_CURRENT_BIT); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + + glLoadIdentity(); + glTranslatef(0.0f,0.0f,-1.0f); + glColor4f(gEnvironment->instrumentColor.x,gEnvironment->instrumentColor.y,gEnvironment->instrumentColor.z,opacity*(1-gConfig->hudTransparency)); + + glTranslatef(xPos,yPos,0); + glScalef(size*0.5,size*0.5,0); + + if(redline) + { + float redlineAngle=(gauge->gaugeZero-2*gauge->gaugeZero*(redline/gauge->gaugeMax)+90)*kDegreeRadians; + + float t=tanf(-redlineAngle); + + TexturesSelectTex(FileGetReference("tachoredline.tif")); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP); + + glBegin(GL_TRIANGLES); + glTexCoord2d(0.5,0.5); glVertex2f(0,0); + glTexCoord2d(1,1); glVertex2f(1,-1); + glTexCoord2d(1,0.5-0.5*t); glVertex2f(1,t); + glEnd(); + } + + TexturesSelectTex(gauge->gaugeTexture); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex2f(1,-1); + glTexCoord2d(1,0); glVertex2f(1,1); + glTexCoord2d(0,1); glVertex2f(-1,-1); + glTexCoord2d(0,0); glVertex2f(-1,1); + glEnd(); + + float tachoAngle=(gauge->gaugeZero-2*gauge->gaugeZero*(value/gauge->gaugeMax))*kDegreeRadians; + float width=gauge->pointerWidth; + float c=cos(tachoAngle); + float s=sin(tachoAngle); + + TexturesSelectTex(gauge->pointerTexture); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex2f(-s+c*width,-c-s*width); + glTexCoord2d(1,0); glVertex2f(s+c*width,c-s*width); + glTexCoord2d(0,1); glVertex2f(-s-c*width,-c+s*width); + glTexCoord2d(0,0); glVertex2f(s-c*width,c+s*width); + glEnd(); + + glPopAttrib(); + glPopMatrix(); + gTexturesQualityModifier=0; +} + +#define kPanelSize 0.32 +#define kPanelOffsetX -0.02 +#define kPanelOffsetY 0.08 +//Render the car status panels (tacho,speedo) on screen +void CarRenderPanels(tGameEntity *carEntity,float opacity) +{ + tCarPhysics *phys=(tCarPhysics*)carEntity->physics; + tCarDefinition *car=&(phys->car); + tGaugesList *gauges=(tGaugesList*)FileGetParsedDataPtr(FileGetReference(kGaugesFileName),kParserTypeGaugesListDesc,sizeof(tGaugesList)); + float width,c,s; + +//Tacho + int tachoGauge=-1; + float gaugeFit=INFINITY; + for(int i=0;inumGauges;i++) + if(gauges->gauges[i].type==kGaugeTypeTacho) + { + int fit=gauges->gauges[i].gaugeMax-car->maxRPM; + if(fit>=0&&fitnumGauges;i++) + if(gauges->gauges[i].type==kGaugeTypeTacho) + { + int fit=fabs(gauges->gauges[i].gaugeMax-car->maxRPM); + if(fitgauges+tachoGauge,phys->rpm,(0.5-kPanelOffsetX-0.5*kPanelSize),-(0.5-kPanelOffsetY-0.5*kPanelSize),kPanelSize,opacity,car->maxRPM*0.9); + +//Speedo + int speedoGauge=-1; + float maxV=car->maxRPM/(car->gearRatios[car->numGears-1]*car->finalDriveRatio)*car->wheels[0].radius*2*PI/60*0.9; + gaugeFit=INFINITY; + for(int i=0;inumGauges;i++) + if(gauges->gauges[i].type==(gConfig->metricUnits?kGaugeTypeSpeedoMetric:kGaugeTypeSpeedo)) + { + int fit=gauges->gauges[i].gaugeMax-maxV; + if(fit>=0&&fitnumGauges;i++) + if(gauges->gauges[i].type==(gConfig->metricUnits?kGaugeTypeSpeedoMetric:kGaugeTypeSpeedo)) + speedoGauge=i; + if(speedoGauge!=-1) + RenderGauge(gauges->gauges+speedoGauge,~carEntity->velo,-(0.5-kPanelOffsetX-0.5*kPanelSize),-(0.5-kPanelOffsetY-0.5*kPanelSize),kPanelSize,opacity,0); + + +//Selected Gear + if(phys->gear==-1) + TextPrintfToBufferFormatedColored(Vector(0.8,-0.9),0.07,kTextAlignLeft,1,1,1,opacity,"R"); + else if(phys->gear==0) + TextPrintfToBufferFormatedColored(Vector(0.8,-0.9),0.07,kTextAlignLeft,1,1,1,opacity,"N"); +/* else if(gGameInfo->playerAutomatic) + TextPrintfToBufferFormated(Vector(0.8,-0.9),0.07,kTextAlignLeft,"D");*/ + else + TextPrintfToBufferFormatedColored(Vector(0.8,-0.9),0.07,kTextAlignLeft,1,1,1,opacity,"%d",phys->gear); + + #ifdef __POLYCOUNT + gPolyCount+=8; + #endif + +} + +#define kShiftAnimationTime 0.3 +#define kArmNumEdges 6 +#define kArmRadius 0.04 + +void RenderArm(tVector3 point1,tVector3 point2,tVector3 point3) +{ + tVector3 dir12=!(point2-point1); + tVector3 dir13=!(point3-point1); + tVector3 dir23=!(point3-point2); + tVector3 dir1=!(dir12%Vector(0,1,0)); + tVector3 dir2=!(dir13%Vector(0,1,0)); + tVector3 dir3=!(dir23%Vector(0,1,0)); + tMatrix3 mat1; + tMatrix3 mat2; + tMatrix3 mat3; + RotationVectorToMatrix(-dir12*((2*PI)/(kArmNumEdges)),mat1); + RotationVectorToMatrix(-dir13*((2*PI)/(kArmNumEdges)),mat2); + RotationVectorToMatrix(-dir23*((2*PI)/(kArmNumEdges)),mat3); + for(int i=0;iphysics; + tCarDefinition *car=&(phys->car); + + if(car->noDriverModel!=1) + { + tVector3 innerShoulder=Vector(-0.13*sign(car->driverPos.x),0.365,-0.047)+car->driverPos; + tVector3 outerShoulder=Vector(0.13*sign(car->driverPos.x),0.365,-0.047)+car->driverPos; + tMatrix3 steeringDir; + MatrixIdentity(steeringDir); + MatrixRotZ(steeringDir,-car->steeringWheelTurns*PI*phys->steering); + MatrixRotX(steeringDir,car->steeringWheelAngle*(2*PI/360.0)); + + //hands on steering wheel + tVector3 innerHand=Vector(-car->steeringWheelRadius*sign(car->driverPos.x),0,0)*steeringDir+car->steeringWheelPos; + tVector3 outerHand=Vector(car->steeringWheelRadius*sign(car->driverPos.x),0,0)*steeringDir+car->steeringWheelPos; + + //hand on handbrake + if(phys->handbrake) + innerHand=Vector(car->driverPos.x-sign(car->driverPos.x)*0.47 + ,car->driverPos.y-0.3+0.3*phys->handbrake + ,car->driverPos.z+0.3); + + //hand on shifter + float shiftDelay=gFrameCount*kFrameTime-phys->lastGearSwitch; + if(shiftDelaydriverPos.x-sign(car->driverPos.x)*0.47 + ,car->driverPos.y+0.13 + ,car->driverPos.z+0.6-0.3*(shiftDelay/kShiftAnimationTime)); + + float maxArmLength=~(car->steeringWheelPos-outerShoulder); + tVector3 innerArm=innerHand-innerShoulder; + tVector3 outerArm=outerHand-outerShoulder; + float innerArmLength=~innerArm; + float outerArmLength=~outerArm; + float innerArmBend=innerArmLength>=maxArmLength?0:sqrt(sqr(maxArmLength)-sqr(innerArmLength))*0.5; + float outerArmBend=outerArmLength>=maxArmLength?0:sqrt(sqr(maxArmLength)-sqr(outerArmLength))*0.5; + tVector3 innerElbow=innerShoulder+innerArm*0.5+((innerHand.z>innerShoulder.z)?1:-1)*!((innerArm)%Vector(0,1,0))*innerArmBend; + tVector3 outerElbow=outerShoulder+outerArm*0.5-((outerHand.z>outerShoulder.z)?1:-1)*!((outerArm)%Vector(0,1,0))*outerArmBend; + + glPushAttrib(GL_LIGHTING_BIT+GL_CURRENT_BIT+GL_TEXTURE_BIT); + TexturesSelectTextureUnit(2); + glDisable(GL_TEXTURE_2D); + TexturesSelectTextureUnit(1); + glDisable(GL_TEXTURE_2D); + TexturesSelectTextureUnit(0); + glDisable(GL_TEXTURE_2D); + GLfloat col[4]={0.1,0.1,0.1,1}; + glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,col); + RenderArm(innerShoulder,innerElbow,innerHand); + RenderArm(outerShoulder,outerElbow,outerHand); + glPopAttrib(); + } + + if(!car->noDriverModel){ + tVector3 driverPos=car->driverPos*carEntity->dir+carEntity->pos; + SetupTranslation(driverPos,carEntity->dir); + DrawModel(FileGetReference("driver.mdl"),true,0); + } +} + + + +void CarRenderEntityGhost(tGameEntity *carEntity) +{ + gGhostShine=true; + tCarPhysics *phys=(tCarPhysics*)carEntity->physics; + tCarDefinition *car=&(phys->car); + + //draw the car model + if(gConfig->interiorDisplay||gCameraMode==kCameraCockpitCarHidden) + DrawModel(car->model,kTransparencyGhost,phys->color); + else + DrawModel(car->model,kTransparencyOff,phys->color); + + //draw add-ons. + for(int i=0;inumAddOns;i++) + //is this add-on installed? + if(((phys->addOns|car->initialAddOns)>>i)&1) + //do we need to draw a model? + if(car->addOns[i].hasGraphic) + if(gConfig->interiorDisplay>0||gCameraMode==kCameraCockpitCarHidden) + DrawModel(car->addOns[i].model,kTransparencyGhost,phys->color); + else + DrawModel(car->addOns[i].model,kTransparencyOff,phys->color); + + //draw the car's wheels + for(int i=0;inumWheels;i++){ + tMatrix3 wheelDir,brakeDir; + MatrixIdentity(wheelDir); + + //correctly rotate wheel + MatrixScale(wheelDir,car->wheels[i].width/1,car->wheels[i].radius/1,car->wheels[i].radius/1); + MatrixCopy(wheelDir,brakeDir); + MatrixRotX(wheelDir,-sign(car->wheels[i].pos.x)*phys->wheels[i].rotation); + MatrixRotY(wheelDir,(car->wheels[i].pos.x<0?0:PI)+phys->wheels[i].angle); + MatrixRotZ(wheelDir,-sign(car->wheels[i].pos.x)*car->wheels[i].tilt); + MatrixMult(wheelDir,carEntity->dir,wheelDir); + + tVector3 wheelPos=car->wheels[i].pos; + wheelPos.y-=phys->wheels[i].suspension; + wheelPos=wheelPos*carEntity->dir+carEntity->pos; + SetupTranslation(wheelPos,wheelDir); + + gGlowFactor=phys->wheels[i].glow; + gBlurMapFactor=fabs(phys->wheels[i].angularVelo/(2*PI*12)); + if(gBlurMapFactor>1)gBlurMapFactor=1; + + if(gConfig->interiorDisplay>0||gCameraMode==kCameraCockpitCarHidden) + DrawModel(car->wheels[i].model,kTransparencyGhost,car->wheels[i].texture); + else + DrawModel(car->wheels[i].model,kTransparencyOff,car->wheels[i].texture); + } + gGhostShine=false; +} + +void DrawLicensePlate(char *text) +{ + GLfloat specularReflectance[4]={1,1,1,1}; + GLfloat ambientReflectance[4]={0.75,0.75,0.75,1}; + GLfloat diffuseReflectance[4]={0.8,0.8,0.8,1}; + glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,diffuseReflectance); + glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT,ambientReflectance); + glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,specularReflectance); + glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,30); + + glNormal3f(0,0,1); + TexturesSelectTex(FileGetReference("license.pct")); + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(0,0); glVertex3f(-0.3,-0.18,-0.02); + glTexCoord2d(1,0); glVertex3f(0.3,-0.18,-0.02); + glTexCoord2d(0,1); glVertex3f(-0.3,0.0,-0.02); + glTexCoord2d(1,1); glVertex3f(0.3,0.0,-0.02); + glEnd(); + + glDepthMask(0); + TextDrawSimple(text,0.56,0.18,kTextAlignMiddle); + glDepthMask(1); +} + +//render a car. +void CarRenderEntity(tGameEntity *carEntity,int allowTransparency,int showDriver) +{ + gTexturesQualityModifier=-2; + tCarPhysics *phys=(tCarPhysics*)carEntity->physics; + tCarDefinition *car=&(phys->car); + + if(car->numColors>phys->color) + car->colorLoaded[phys->color]=true; + + int drawDirt=gEnvironment->dirtEnable; + if(gMapInfo) + if(gMapInfo->dirtEnable) + drawDirt=true; + if(drawDirt) + if(TexturesSelectTextureUnit(2)) + { + glEnable(GL_TEXTURE_2D); + glEnable(GL_TEXTURE_GEN_S); + glEnable(GL_TEXTURE_GEN_T); + TexturesSelectTex(gMapInfo->dirtEnable?gMapInfo->dirtMap:gEnvironment->dirtMap); + glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR); + glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR); + float extends=ModelGetMaxExtends(car->model); + GLfloat plane1[4]={phys->dirtStretch[0]/extends,0,phys->dirtStretch[1]/extends,0}; + GLfloat plane2[4]={0.5*phys->dirtStretch[2]/extends,0.5*phys->dirtStretch[3]/extends,phys->dirtStretch[4]/extends,0}; + glTexGenfv(GL_S,GL_OBJECT_PLANE,plane1); + glTexGenfv(GL_T,GL_OBJECT_PLANE,plane2); + + GLfloat color[4]={1,1,1,phys->dirt*0.01*(gMapInfo->dirtEnable?gMapInfo->dirtIntensity:gEnvironment->dirtIntensity)}; + glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE_ARB); + glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB_ARB,GL_INTERPOLATE_ARB); + glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE2_RGB_ARB,GL_CONSTANT_ARB); + glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND2_RGB_ARB,GL_SRC_ALPHA); + glTexEnvfv(GL_TEXTURE_ENV,GL_TEXTURE_ENV_COLOR,color); + + TexturesSelectTextureUnit(0); + } + + gTunnelFactor=phys->echo; + //draw the car model + DrawModel(car->model,allowTransparency,phys->color); + + + if(drawDirt) + if(TexturesSelectTextureUnit(2)){ + glDisable(GL_TEXTURE_2D); + TexturesSelectTextureUnit(0); + } + + //draw add-ons. + for(int i=0;inumAddOns;i++) + //is this add-on installed? + if(((phys->addOns|car->initialAddOns)>>i)&1) + //do we need to draw a model? + if(car->addOns[i].hasGraphic) + DrawModel(car->addOns[i].model,allowTransparency,phys->color); + + //if we have transparency, we need to draw the interior + if(allowTransparency) + { + //draw interior model + DrawModel(car->interiorModel,true,phys->color); + + //if we aren't "inside" the driver, render him. + if(showDriver) + CarRenderDriver(carEntity); + + //and the steering wheel + + if(car->steeringWheelRadius) + { + tMatrix3 steeringDir; + tVector3 steeringWheelPos=car->steeringWheelPos*carEntity->dir+carEntity->pos; + MatrixIdentity(steeringDir); + MatrixRotZ(steeringDir,-car->steeringWheelTurns*PI*phys->steering); + MatrixRotX(steeringDir,car->steeringWheelAngle*(2*PI/360.0)); + MatrixMult(steeringDir,carEntity->dir,steeringDir); + SetupTranslation(steeringWheelPos,steeringDir); + + if(tTransparentPoly *p=GetNextTransparentPoly(true)){ + p->v[0].texel=Vector(0,1); + p->v[1].texel=Vector(0,0); + p->v[2].texel=Vector(1,1); + p->v[0].normal=Vector(0,0,1)*gTransformDir; + p->v[1].normal=Vector(0,0,1)*gTransformDir; + p->v[2].normal=Vector(0,0,1)*gTransformDir; + p->v[0].vertex=Vector(-car->steeringWheelRadius,-car->steeringWheelRadius,0)*gTransformDir+gTransformPos; + p->v[1].vertex=Vector(-car->steeringWheelRadius,car->steeringWheelRadius,0)*gTransformDir+gTransformPos; + p->v[2].vertex=Vector(car->steeringWheelRadius,-car->steeringWheelRadius,0)*gTransformDir+gTransformPos; + p->mat->flags=kMaterialBothSides+kMaterialUseAlphaChannel+kMaterialDisableWrapS; + p->mat->shininess=0; + p->mat->specular=Vector(0,0,0); + p->mat->ambient=Vector(0.6,0.6,0.6); + p->mat->diffuse=Vector(0.4,0.4,0.4); + p->mat->texRef=car->steeringWheelTexture; + if(tTransparentPoly *p2=GetNextTransparentPoly(false)){ + p2->v[0].texel=Vector(0,0); + p2->v[1].texel=Vector(1,1); + p2->v[2].texel=Vector(1,0); + p2->v[0].normal=Vector(0,0,1)*gTransformDir; + p2->v[1].normal=Vector(0,0,1)*gTransformDir; + p2->v[2].normal=Vector(0,0,1)*gTransformDir; + p2->v[0].vertex=Vector(-car->steeringWheelRadius,car->steeringWheelRadius,0)*gTransformDir+gTransformPos; + p2->v[1].vertex=Vector(car->steeringWheelRadius,-car->steeringWheelRadius,0)*gTransformDir+gTransformPos; + p2->v[2].vertex=Vector(car->steeringWheelRadius,car->steeringWheelRadius,0)*gTransformDir+gTransformPos; + p2->mat=p->mat; + } + } + } + } + + //draw License plates + if(phys->plateName) + { + TextLoadFont(FileGetReference("license.font")); + if(!VectorZero(car->frontLicensePlatePos)) + { + tVector3 licensePlatePos=car->frontLicensePlatePos*carEntity->dir+carEntity->pos; + SetupTranslation(licensePlatePos,carEntity->dir); + DrawLicensePlate(phys->plateName); + } + if(!VectorZero(car->rearLicensePlatePos)) + { + tVector3 licensePlatePos=car->rearLicensePlatePos*carEntity->dir+carEntity->pos; + tMatrix3 licenseDir; + MatrixIdentity(licenseDir); + MatrixRotY(licenseDir,PI); + MatrixMult(licenseDir,carEntity->dir,licenseDir); + SetupTranslation(licensePlatePos,licenseDir); + DrawLicensePlate(phys->plateName); + } + TextLoadFont(FileGetReference("test.font")); + } + + gTexturesQualityModifier=-4; + //draw the car's wheels + for(int i=0;inumWheels;i++){ + tMatrix3 wheelDir,brakeDir; + MatrixIdentity(wheelDir); + + //correctly rotate wheel + MatrixScale(wheelDir,car->wheels[i].width/1,car->wheels[i].radius/1,car->wheels[i].radius/1); + MatrixCopy(wheelDir,brakeDir); + MatrixRotX(wheelDir,-sign(car->wheels[i].pos.x)*phys->wheels[i].rotation); + MatrixRotY(wheelDir,(car->wheels[i].pos.x<0?0:PI)+phys->wheels[i].angle); + MatrixRotZ(wheelDir,-sign(car->wheels[i].pos.x)*car->wheels[i].tilt); + MatrixMult(wheelDir,carEntity->dir,wheelDir); + + tVector3 wheelPos=car->wheels[i].pos; + wheelPos.y-=phys->wheels[i].suspension; + wheelPos=wheelPos*carEntity->dir+carEntity->pos; + SetupTranslation(wheelPos,wheelDir); + + gGlowFactor=phys->wheels[i].glow; + gBlurMapFactor=fabs(phys->wheels[i].angularVelo/(2*PI*12)); + if(gBlurMapFactor>1)gBlurMapFactor=1; + + glMatrixMode(GL_TEXTURE); + glPushMatrix(); + tMatrix4 m; + MatrixIdentity(m); + MatrixTranslate(m,0,0,0.25+0.5*(gBlurMapFactor*2<1?gBlurMapFactor*2:1)); + glLoadMatrixf((float*)m); + + DrawModel(car->wheels[i].model,allowTransparency,car->wheels[i].texture); + + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + + if(allowTransparency) + { + MatrixRotX(brakeDir,(car->wheels[i].pos.x<0?0:PI)); + MatrixRotY(brakeDir,(car->wheels[i].pos.x<0?0:PI)+phys->wheels[i].angle); + MatrixRotZ(brakeDir,-sign(car->wheels[i].pos.x)*car->wheels[i].tilt); + MatrixMult(brakeDir,carEntity->dir,brakeDir); + SetupTranslation(wheelPos,brakeDir); + if(car->wheels[i].customBrakeModel>0) + DrawModel(car->wheels[i].customBrakeModel,allowTransparency,0); + else + DrawModel(FileGetReference("brakedischousing.mdl"),allowTransparency,0); + } + } + gTexturesQualityModifier=0; +} + +#define kNumWheelSides 12 + +void CarRenderWheelShadowPass(float width,float radius,float shadowLength,int cw) +{ + tVector3 lightDir=gEnvironment->lightDir; + if(gMapEnv) + if(!VectorZero(gMapEnv->lightDir)) + lightDir=gMapEnv->lightDir; + tMatrix3 tr; + MatrixTranspose(gTransformDir,tr); + lightDir=lightDir*tr; + tVector3 shadowVector=lightDir*shadowLength; + + if(lightDir.x!=0) + { + tVector3 lightDirFlat=lightDir; + lightDirFlat.x=0; + lightDirFlat=!lightDirFlat; + tVector3 normal=lightDirFlat%Vector(1,0,0); + + lightDirFlat=lightDirFlat*radius; + normal=normal*radius; + + float dir=-sign(lightDir.x)*width/2; + + glFrontFace(lightDir.x<0?(cw?GL_CW:GL_CCW):(cw?GL_CCW:GL_CW)); + + glBegin(GL_TRIANGLE_STRIP); + for(int i=0;i<=kNumWheelSides/2;i++) + { + float f=((float)i/kNumWheelSides)*2*PI; + float s=sin(f); + float c=cos(f); + tVector3 v=Vector(dir,c*normal.y+s*lightDirFlat.y,c*normal.z+s*lightDirFlat.z); + glVertex3fv(&v.x); + glVertex3fv(&(v-shadowVector).x); + } + for(int i=kNumWheelSides/2;i<=kNumWheelSides;i++) + { + float f=((float)i/kNumWheelSides)*2*PI; + float s=sin(f); + float c=cos(f); + tVector3 v=Vector(-dir,c*normal.y+s*lightDirFlat.y,c*normal.z+s*lightDirFlat.z); + glVertex3fv(&v.x); + glVertex3fv(&(v-shadowVector).x); + } + tVector3 v=Vector(dir,normal.y,normal.z); + glVertex3fv(&v.x); + glVertex3fv(&(v-shadowVector).x); + glEnd(); + } + + #ifdef __POLYCOUNT + gPolyCount+=kNumWheelSides*2+4; + #endif +} + +void CarRenderWheelShadow(float width,float radius,float shadowLength) +{ + //disable actual drawing + glDisable(GL_LIGHTING); + glDisable(GL_FOG); + glDepthMask(GL_FALSE); + glColorMask(0, 0, 0, 0); + + //enable stencil buffer drawing + glEnable(GL_STENCIL_TEST); + glStencilFunc(GL_ALWAYS, 0, 0xffffffff); + + //zPass Rendering + + glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); + CarRenderWheelShadowPass(width,radius,shadowLength,false); + + glStencilOp(GL_KEEP, GL_KEEP, GL_DECR); + CarRenderWheelShadowPass(width,radius,shadowLength,true); + + glDepthMask(GL_TRUE); + glColorMask(1, 1, 1, 1); +} + +//draw the car's shadow +void CarRenderEntityShadow(tGameEntity *carEntity,float shadowLength) +{ + tCarPhysics *phys=(tCarPhysics*)carEntity->physics; + tCarDefinition *car=&(phys->car); + tFileRef nullModel=FileGetReference("null.mdl"); + if(shadowLength==0) + { + shadowLength=GetGroundOffset(carEntity->pos,&carEntity->lastRoadIndex,0,0)+kCarMinShadow; + shadowLength*=1/(gEnvironment->lightDir*Vector(0,1,0)); + } + + //draw the shadow for the car model + if(car->shadowModel) + DrawModelShadow(car->shadowModel,shadowLength); + else + DrawModelShadow(car->model,shadowLength); + + //draw add-ons shadows. + for(int i=0;inumAddOns;i++) + //is this add-on installed? + if(((phys->addOns|car->initialAddOns)>>i)&1) + //do we need to draw a model? + if(car->addOns[i].hasGraphic) + DrawModelShadow(car->addOns[i].model,shadowLength); + + //draw the shadow for the wheels + for(int i=0;inumWheels;i++) + if(car->wheels[i].model!=nullModel&&!car->wheels[i].noShadow) + { + tMatrix3 wheelDir; + MatrixIdentity(wheelDir); + //MatrixScale(wheelDir,car->wheels[i].widthRatio,1,1); + MatrixRotX(wheelDir,sign(car->wheels[i].pos.x)*phys->wheels[i].rotation); + MatrixRotY(wheelDir,PI+phys->wheels[i].angle); + MatrixRotZ(wheelDir,-sign(car->wheels[i].pos.x)*car->wheels[i].tilt); + MatrixMult(wheelDir,carEntity->dir,wheelDir); + tVector3 wheelPos=car->wheels[i].pos; + wheelPos.y-=phys->wheels[i].suspension; + wheelPos=wheelPos*carEntity->dir+carEntity->pos; + SetupTranslation(wheelPos,wheelDir); + CarRenderWheelShadow(car->wheels[i].width*sqr(gStencilZoom),car->wheels[i].radius*sqr(gStencilZoom),shadowLength); + } +} + diff --git a/source/rendercar.h b/source/rendercar.h new file mode 100644 index 0000000..d0e4af9 --- /dev/null +++ b/source/rendercar.h @@ -0,0 +1,30 @@ +#ifndef __RENDERCAR +#define __RENDERCAR + +#include "entities.h" + +#define kGaugesFileName "gauges.cfg" + +enum{ + kGaugeTypeTacho=0, + kGaugeTypeSpeedo=1, + kGaugeTypeSpeedoMetric=2 +}; + +typedef struct{ + tFileRef gaugeTexture,pointerTexture; + float pointerWidth,gaugeZero,gaugeMax; + int type; +} tGauge; + +typedef struct{ + int numGauges; + tGauge *gauges; +} tGaugesList; + +void CarRenderPanels(tGameEntity *carEntity,float opacity); +void CarRenderEntity(tGameEntity *carEntity,int allowTransparency,int showDriver); +void CarRenderEntityGhost(tGameEntity *carEntity); +void CarRenderEntityShadow(tGameEntity *carEntity,float shadowLength); + +#endif \ No newline at end of file diff --git a/source/renderframe.cpp b/source/renderframe.cpp new file mode 100755 index 0000000..1780757 --- /dev/null +++ b/source/renderframe.cpp @@ -0,0 +1,731 @@ +//renderframe.cpp +//renders one graphics frame + +#include +#include +#include "entities.h" +#include "environment.h" +#include "fileio.h" +#include "sky.h" +#include "models.h" +#include "roads.h" +#include "text.h" +#include "screen.h" +#include "renderframe.h" +#include "rendercar.h" +#include "particles.h" +#include "tracks.h" +#include "stencil.h" +#include "config.h" +#include "transparency.h" +#include "gamemem.h" +#include "collision.h" +#include "gameinitexit.h" +#include "carphysics.h" +#include "random.h" +#include "gameframe.h" +#include "textures.h" +#include "lights.h" + +#ifdef __POLYCOUNT +int gPolyCount; +#endif + +int gCameraMode,gCameraReverse,gClipEnable=true; +tMatrix3 gTransformDir; +tVector3 gTransformPos; + +void Beer(tMatrix4 m) +{ + MatrixRotX(m,0.1*sin(gFrameCount*0.01)); + MatrixRotY(m,0.02*sin(gFrameCount*0.1)); + MatrixRotZ(m,0.3*sin(gFrameCount*0.008)); + MatrixScale(m,1+0.1*sin(gFrameCount*0.01),1+0.4*cos(gFrameCount*0.001),1-0.15*sin(gFrameCount*0.02)); + + tMatrix4 mt; + MatrixIdentity(mt); + MatrixRotX(mt,0.01*sin(gFrameCount*0.01)); + MatrixRotY(mt,0.002*sin(gFrameCount*0.1)); + MatrixRotZ(mt,0.03*sin(gFrameCount*0.008)); + glMatrixMode(GL_TEXTURE); + glLoadMatrixf((float*)mt); + glMatrixMode(GL_MODELVIEW); +} + +//Set up the Modelview matrix to draw the object at objPos, rotated by objDir +void SetupTranslation(tVector3 objPos,tMatrix3 objDir) +{ + tMatrix4 m1,m2; + tMatrix3 m,r,invTransformDir; + MatrixIdentity(m1); + MatrixMult(m1,objDir,m2); + MatrixTranslateVector(m2,objPos-gCameraEntity->pos); + MatrixTranspose(gCameraEntity->dir,m); + r[0][0]=-1;r[0][1]=0;r[0][2]=0; + r[1][0]=0; r[1][1]=1;r[1][2]=0; + r[2][0]=0; r[2][1]=0;r[2][2]=-1; + MatrixMult(m,r,m); + MatrixMult(m2,m,m1); +#ifndef __TARGET_TOOLAPP + if(gGameInfo) + if(gGameInfo->carsOnSpeed) + Beer(m1); +#endif + glLoadMatrixf((float*)m1); + + MatrixCopy(objDir,gTransformDir); + gTransformPos=objPos; + + MatrixTranspose(objDir,invTransformDir); + GLfloat globalLightPosition[4]; + *(tVector3*)globalLightPosition=gEnvironment->lightDir*invTransformDir; + if(gMapEnv) + if(!VectorZero(gMapEnv->lightDir)) + *(tVector3*)globalLightPosition=gMapEnv->lightDir*invTransformDir; + + globalLightPosition[3]=0; + glLightfv(GL_LIGHT0,GL_POSITION,globalLightPosition); + + if(gViewedEntity) + { + float objDist=~(objPos-gViewedEntity->pos); + if(objDist==0) + gShineFactor=0; + else + { + gShineFactor=(objPos-gViewedEntity->pos)*(1/objDist)**MatrixGetZVector(gViewedEntity->dir); + // gShineFactor=sqr(sqr(sqr(sqr(gShineFactor))))*gShineFactor; + if(objDist>100) + gShineFactor-=(objDist-100)*0.005; + if(gShineFactor<0)gShineFactor=0; + } + } +} + +//Set up the Modelview matrix to draw objects at world coordinates +void SetupWorldTranslation() +{ + tMatrix4 m1,m2; + tMatrix3 m,r; + MatrixIdentity(m1); + MatrixTranslateVector(m1,-gCameraEntity->pos); + MatrixTranspose(gCameraEntity->dir,m); + r[0][0]=-1;r[0][1]=0;r[0][2]=0; + r[1][0]=0; r[1][1]=1;r[1][2]=0; + r[2][0]=0; r[2][1]=0;r[2][2]=-1; + MatrixMult(m,r,m); + MatrixMult(m1,m,m2); +#ifndef __TARGET_TOOLAPP + if(gGameInfo) + if(gGameInfo->carsOnSpeed) + Beer(m2); +#endif + glLoadMatrixf((float*)m2); + + GLfloat globalLightPosition[4]; + *(tVector3*)globalLightPosition=gEnvironment->lightDir; + if(gMapEnv) + if(!VectorZero(gMapEnv->lightDir)) + *(tVector3*)globalLightPosition=gMapEnv->lightDir; + globalLightPosition[3]=0; + glLightfv(GL_LIGHT0,GL_POSITION,globalLightPosition); + + MatrixIdentity(gTransformDir); + gTransformPos=Vector(0,0,0); +} + +//calculates the clipping planes from the camera position and rotation +void SetupClipPlanes(tGameEntity *camera,tVector3 *clipPlanes) +{ + clipPlanes[kClipBasePoint]=Vector(0,0,-kClipSaveDistance); + clipPlanes[kClipFarPoint]=Vector(0,0,ClipDistance()-kClipSaveDistance); + + clipPlanes[kClipXPlane]=Vector(1,0,0); + clipPlanes[kClipYPlane]=Vector(0,1,0); + clipPlanes[kClipRearPlane]=Vector(0,0,1); + + clipPlanes[kClipRightPlane]=Vector(-cos(gFOVX*0.5),0,sin(gFOVX*0.5)); + clipPlanes[kClipLeftPlane]=Vector(-clipPlanes[kClipRightPlane].x,0,clipPlanes[kClipRightPlane].z); + + clipPlanes[kClipTopPlane]=Vector(0,cos(gFOVY*0.5),sin(gFOVY*0.5)); + clipPlanes[kClipBotPlane]=Vector(0,-clipPlanes[kClipTopPlane].y,clipPlanes[kClipTopPlane].z); + + for(int i=0;idir; + } + clipPlanes[kClipBasePoint]=clipPlanes[kClipBasePoint]+camera->pos; +} + +//tests if the a point is inside the clipping volume given by clipPlanes +int ClipPoint(tVector3 *clipPlanes,tVector3 *point) +{ + if(!gClipEnable) + return false; + + tVector3 relPoint=*point-clipPlanes[kClipBasePoint]; + int clip=0; + + for(int plane=kClipRearPlane;plane0) + clip+=1<distance) + clip+=1<prev)->next=obj2; + ((tGameEntity*)obj2->next)->prev=obj1; + obj1->next=obj2->next; + obj2->prev=obj1->prev; + obj1->prev=obj2; + obj2->next=obj1; +} + +void SortEntities() +{ + tGameEntity *entity=(tGameEntity*)gFirstEntity->next; + while(entity!=gFirstEntity) + { + if(entity->renderType==kRenderTypeModel||entity->renderType==kRenderTypeCar||entity->renderType==kRenderTypeGhost) + { + tVector3 center=ModelGetCenter(entity->renderData); + center=center*entity->dir; + center=center+entity->pos; + entity->zDist=(center-gCameraEntity->pos)**MatrixGetZVector(gCameraEntity->dir); + } + else + entity->zDist=(entity->pos-gCameraEntity->pos)**MatrixGetZVector(gCameraEntity->dir); + entity=(tGameEntity*)entity->next; + } + + tGameEntity *start=(tGameEntity*)gFirstEntity->next; + tGameEntity *end=gFirstEntity; + int change; + do{ //since in most cases there wont be much change a shaking sort is often the fastest + entity=start; + change=false; + while(entity->next!=end) + { + tGameEntity *next=(tGameEntity*)entity->next; + if(next->zDist>entity->zDist) + {SwitchEntities(entity,next);change=true;} + else + entity=next; + } + if(change) + { + end=(tGameEntity*)end->prev; + entity=end; + while(entity->prev!=start) + { + tGameEntity *prev=(tGameEntity*)entity->prev; + if(prev->zDistzDist) + SwitchEntities(prev,entity); + else + entity=prev; + } + start=(tGameEntity*)start->next; + if(start==end) change=false; + } + }while(change); + +} + +void RenderObjectPass(tVector3 *clipPlanes) +{ + glPushAttrib(GL_LIGHTING_BIT+GL_TEXTURE_BIT+GL_POLYGON_BIT+GL_COLOR_BUFFER_BIT); + + tGameEntity *drawEntity=(tGameEntity*)gFirstEntity->prev; + while(drawEntity!=gFirstEntity) + { + if(drawEntity->renderType==kRenderTypeModel||drawEntity->renderType==kRenderTypeCar||drawEntity->renderType==kRenderTypeGhost) + { + float distance=ModelGetCenterMaxExtends(drawEntity->renderData); + tVector3 center=ModelGetCenter(drawEntity->renderData); + center=center*drawEntity->dir; + center=center+drawEntity->pos; + if(drawEntity->zDist>-distance||!gClipEnable) + if(!ClipPointDistanced(clipPlanes,¢er,distance)) + { + SetupTranslation(drawEntity->pos,drawEntity->dir); + switch(drawEntity->renderType) + { + case kRenderTypeModel: + DrawModel(drawEntity->renderData,true,*((int*)drawEntity->physics)); + break; + case kRenderTypeCar: + { + if(gCameraMode==kCameraCockpitCarHidden&&drawEntity==gCarEntities[gReplayViewedEntityID]&&sqr(gCameraEntity->pos-drawEntity->pos)gfxDynamics*200.0f; + if(drawEntity->zDistinteriorDisplay) + drawInterior=false; + CarRenderEntity(drawEntity,drawInterior,drawEntity->controlType!=kControlTypeNone&&!(drawEntity==gViewedEntity&&gCameraMode==kCameraCockpit)); + if(gConfig->showPlayerNames&&gGameInfo->network&&(drawEntity!=gCarEntities[gReplayViewedEntityID])) + { + tVector3 pos=drawEntity->pos+Vector(0,3,0); + pos=pos-gCameraEntity->pos; + tMatrix3 inv; + MatrixTranspose(gCameraEntity->dir,inv); + pos=pos*inv; + float size=0.8+pos.z*0.005; + for(int i=0;inumPlayers;i++) + if(gCarEntities[i]==drawEntity) + TextPrintfToBufferFormatedVector3(pos,size,kTextAlignMiddle,gGameInfo->playerNames[i]); + } + } + break; + case kRenderTypeGhost: + if(!gConfig->noGhost) + CarRenderEntityGhost(drawEntity); + break; + } + } + } + drawEntity=(tGameEntity*)drawEntity->prev; + } + + glPopAttrib(); +} + +#define kEntMinShadow 6 + +void RenderShadowPass(tVector3 *clipPlanes,int highQual) +{ + glPushAttrib(GL_ENABLE_BIT+GL_POLYGON_BIT); + + tGameEntity *drawEntity=(tGameEntity*)gFirstEntity->next; + while(drawEntity!=gFirstEntity&&drawEntity->zDist>-200) + { + if(gConfig->gfxDynamics>=0.2||drawEntity==gViewedEntity) + if(drawEntity->renderType==kRenderTypeModel||drawEntity->renderType==kRenderTypeCar) + if((sqr(drawEntity->pos-gCameraEntity->pos)renderType==kRenderTypeModel) + if(drawEntity->physicsType!=kPhysicsTypeSolid) + shad=false; + else + { + tSolidEntityPhysics *ent=(tSolidEntityPhysics*)gFileTable[drawEntity->physicsData].parsedData; + if(ent->shadowModel==kFileErr) + shad=false; + } + + if(shad) + { + float shadowLength=GetGroundOffset(drawEntity->pos,&drawEntity->lastRoadIndex,0,0)+kEntMinShadow; + shadowLength/=gEnvironment->lightDir.y; + float distance=ModelGetMaxExtends(drawEntity->renderData)+shadowLength; + if(drawEntity->zDist>-distance&&drawEntity->zDistpos,distance)) + { + SetupTranslation(drawEntity->pos,drawEntity->dir); + switch(drawEntity->renderType) + { + case kRenderTypeCar: + if(gCameraMode!=kCameraCockpitCarHidden||drawEntity!=gCarEntities[gReplayViewedEntityID]) + CarRenderEntityShadow(drawEntity,0); + break; + case kRenderTypeModel: + tSolidEntityPhysics *ent=(tSolidEntityPhysics*)gFileTable[drawEntity->physicsData].parsedData; + DrawModelShadow(ent->shadowModel,shadowLength); + break; + } + } + } + } + drawEntity=(tGameEntity*)drawEntity->next; + } + glPopAttrib(); +} + +#define kLightConeClip 200 + +void RenderLightConesPass(tVector3 *clipPlanes) +{ + tGameEntity *drawEntity=(tGameEntity*)gFirstEntity->next; + while(drawEntity!=gFirstEntity&&drawEntity->zDist>-25) + { + if(!ClipPointDistanced(clipPlanes,&drawEntity->pos,kLightConeClip)) + { + switch(drawEntity->renderType) + { + case kRenderTypeCar: + if(drawEntity==gViewedEntity) + { + tCarPhysics *phys=(tCarPhysics*)drawEntity->physics; + tCarDefinition *car=&(phys->car); + RenderEntityLightCones(drawEntity,car->numLights,phys->lightFlags,car->lights); + } + break; + case kRenderTypeModel: + if(drawEntity->physicsType==kPhysicsTypeSolid) + { + tSolidEntityPhysics *ent=(tSolidEntityPhysics*)gFileTable[drawEntity->physicsData].parsedData; + RenderEntityLightCones(drawEntity,ent->numLights,0xffff,ent->lights); + } + break; + } + } + drawEntity=(tGameEntity*)drawEntity->next; + } +} + +void RenderLightsPass(tVector3 *clipPlanes) +{ + gTexturesQualityModifier=-255; + tGameEntity *drawEntity=(tGameEntity*)gFirstEntity->next; + while(drawEntity!=gFirstEntity) + { + SetupTranslation(drawEntity->pos,drawEntity->dir); + switch(drawEntity->renderType) + { + case kRenderTypeCar:{ + tCarPhysics *phys=(tCarPhysics*)drawEntity->physics; + tCarDefinition *car=&(phys->car); + RenderEntityLights(drawEntity,car->numLights,phys->lightFlags,phys->echo,car->lights); + }break; + case kRenderTypeModel: + if(drawEntity->physicsType==kPhysicsTypeSolid) + { + int objFrameCount=gFrameCount+10*((drawEntity->id*31)%17); + int allLightEnable=gEnvironment->spotLightEnable; + if(gMapEnv) + if(gMapEnv->lightEnable) + allLightEnable=true; + int objLightFlags=0x1 + |(((int)(objFrameCount/(2*kFPS))%2)?0x2:0) + |(((int)(objFrameCount/(1*kFPS))%2)?0x4:0) + |(((int)(objFrameCount/(0.5*kFPS))%2)?0x8:0) + |(((int)(objFrameCount/(0.3*kFPS))%4)?0:0x10) + |(allLightEnable?0x20:0); + tSolidEntityPhysics *ent=(tSolidEntityPhysics*)gFileTable[drawEntity->physicsData].parsedData; + RenderEntityLights(drawEntity,ent->numLights,objLightFlags,0,ent->lights); + } + break; + } + drawEntity=(tGameEntity*)drawEntity->next; + } + gTexturesQualityModifier=0; +} + + +void SetupLighting() +{ + if(gCameraEntity) + SetupWorldTranslation(); + + GLfloat globalLightAmbient[4]; + *(tVector3*)globalLightAmbient=gEnvironment->ambient; + globalLightAmbient[3]=1; + + GLfloat globalLightDiffuse[4]; + *(tVector3*)globalLightDiffuse=gEnvironment->diffuse; + globalLightDiffuse[3]=1; + + GLfloat globalLightSpecular[4]; + *(tVector3*)globalLightSpecular=gEnvironment->specular; + globalLightSpecular[3]=1; + + GLfloat globalLightPosition[4]; + *(tVector3*)globalLightPosition=gEnvironment->lightDir; + if(gMapEnv) + if(!VectorZero(gMapEnv->lightDir)) + *(tVector3*)globalLightPosition=gMapEnv->lightDir; + + globalLightPosition[3]=0; + + if(gLightning) + { + globalLightAmbient[0]=1; + globalLightAmbient[1]=1; + globalLightAmbient[2]=1; + globalLightDiffuse[0]=1; + globalLightDiffuse[1]=1; + globalLightDiffuse[2]=1; + globalLightSpecular[0]=1; + globalLightSpecular[1]=1; + globalLightSpecular[2]=1; + } + + glEnable(GL_LIGHT0); + glLightfv(GL_LIGHT0,GL_AMBIENT,globalLightAmbient); + glLightfv(GL_LIGHT0,GL_DIFFUSE,globalLightDiffuse); + glLightfv(GL_LIGHT0,GL_SPECULAR,globalLightSpecular); + glLightfv(GL_LIGHT0,GL_POSITION,globalLightPosition); + + if(gMapEnv) + if(gMapEnv->fogBegin) + { + GLfloat fogColor[4]; + *(tVector3*)fogColor=gMapEnv->fogColor; + fogColor[3]=1; + glFogfv(GL_FOG_COLOR,fogColor); + glFogf(GL_FOG_START,gMapEnv->fogBegin*(1+0.3*gMapEnv->fogOscillation*sin(gFrameCount*kFrameTime*0.7*gMapEnv->fogOscillationSpeed))); + glFogf(GL_FOG_END,gMapEnv->fogEnd*(1+0.5*gMapEnv->fogOscillation*sin(gFrameCount*kFrameTime*0.3*gMapEnv->fogOscillationSpeed))); + } +} + +void DrawFlare(tFileRef texture,float x,float y,float size,float alpha) +{ + glPushAttrib(GL_DEPTH_BUFFER_BIT+GL_LIGHTING_BIT+GL_COLOR_BUFFER_BIT+GL_TEXTURE_BIT+GL_CURRENT_BIT); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE,GL_ONE); + + glLoadIdentity(); + glTranslatef(0.0f,0.0f,-1.0f); + + glTranslatef(x,y,0); + glScalef(size*0.5,size*0.5,0); + + glColor4f(1,1,1,alpha); + + TexturesSelectTex(texture); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex2f(1,-1); + glTexCoord2d(1,0); glVertex2f(1,1); + glTexCoord2d(0,1); glVertex2f(-1,-1); + glTexCoord2d(0,0); glVertex2f(-1,1); + glEnd(); + + glPopAttrib(); +} + +void FlaresDraw1() +{ + if(gEnvironment->flaresEnable) + { + tMatrix3 cameraInverse; + MatrixTranspose(gCameraEntity->dir,cameraInverse); + tVector3 flarePos=gEnvironment->flareDir*cameraInverse; + if(gMapEnv) + if(!VectorZero(gMapEnv->flareDir)) + flarePos=gMapEnv->flareDir*cameraInverse; + + if(flarePos.z<=0)return; + tVector2 flarePosProjected=Vector(flarePos.x/flarePos.z,-flarePos.y/flarePos.z); + + DrawFlare(FileGetReference("flare1.tif"),-flarePosProjected.x*0.5,-flarePosProjected.y*0.5,0.6,1.0); + } +} + +void FlaresDraw2() +{ + if(gEnvironment->flaresEnable) + { + tMatrix3 cameraInverse; + MatrixTranspose(gCameraEntity->dir,cameraInverse); + tVector3 flarePos=gEnvironment->flareDir*cameraInverse; + if(gMapEnv) + if(!VectorZero(gMapEnv->flareDir)) + flarePos=gMapEnv->flareDir*cameraInverse; + + if(flarePos.z<=0)return; + tVector2 flarePosProjected=Vector(flarePos.x/flarePos.z,-flarePos.y/flarePos.z); + + DrawFlare(FileGetReference("flare5.tif"),flarePosProjected.x*1.5,flarePosProjected.y*1.5,0.6,1.0); + DrawFlare(FileGetReference("flare4.tif"),flarePosProjected.x*0.5,flarePosProjected.y*0.5,0.6,1.0); + DrawFlare(FileGetReference("flare3.tif"),flarePosProjected.x*0.25,flarePosProjected.y*0.25,0.6,1.0); + DrawFlare(FileGetReference("flare2.tif"),-flarePosProjected.x*0.1,-flarePosProjected.y*0.1,0.6,1.0); + } +} + +void GameShowInfo(); + +void RenderVisWalls() +{ + SetupWorldTranslation(); + + glColorMask(0,0,0,0); + glDisable(GL_CULL_FACE); + for(int i=0;inumVisWalls;i++) + { + glBegin(GL_TRIANGLES); + glVertex3fv(&(gMapInfo->visWalls[i].a.x)); + glVertex3fv(&(gMapInfo->visWalls[i].b.x)); + glVertex3fv(&(gMapInfo->visWalls[i].c.x)); + + glVertex3fv(&(gMapInfo->visWalls[i].b.x)); + glVertex3fv(&(gMapInfo->visWalls[i].c.x)); + glVertex3fv(&((gMapInfo->visWalls[i].c+(gMapInfo->visWalls[i].b-gMapInfo->visWalls[i].a)).x)); + glEnd(); + } + glEnable(GL_CULL_FACE); + glColorMask(1,1,1,1); +} + +int gLastFrameBlur=false; +int gLastGraphFrame=0; + +#define kMaxBlur 0.925 +void MotionBlur() +{ + float blur; + if(gConfig->carsOnSpeed) + //blur=0.9+0.25*sin(gFrameCount*kFrameTime*0.2); + blur=1; + else + { + if(!gConfig->motionBlur) + return; + blur=((~gCameraEntity->velo-30)/55.0); + } + int blurExp=gFrameCount-gLastGraphFrame-1; + if(blurExp>10)blurExp=10; + if(blurExp<0)blurExp=0; + blur*=pow(kMaxBlur,blurExp); + if(blur>0&&(gGameInfo->arcade==kGameModeArcade||gGameInfo->arcade==kGameModeTurbo||gConfig->carsOnSpeed)&&gConfig->motionBlur) + { + if(blur>1) + blur=1; + glPushAttrib(GL_ENABLE_BIT+GL_CURRENT_BIT); + glDisable(GL_CULL_FACE); + glDisable(GL_LIGHTING); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + glLoadIdentity(); + glColor4f(1,1,1,kMaxBlur*blur); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0,gConfig->screenXSize,0,gConfig->screenYSize,-1,2); + for(int i=0;i<4;i++) + if(gScreenTextures[i].xSize) + { + glBindTexture(GL_TEXTURE_2D,gScreenTextures[i].texture); + if(gLastFrameBlur) + { + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex3f(gScreenTextures[i].xSize+gScreenTextures[i].xOffset, gScreenTextures[i].ySize+gScreenTextures[i].yOffset,1); + glTexCoord2d(1,0); glVertex3f(gScreenTextures[i].xSize+gScreenTextures[i].xOffset, gScreenTextures[i].yOffset,1); + glTexCoord2d(0,1); glVertex3f(0+gScreenTextures[i].xOffset, gScreenTextures[i].ySize+gScreenTextures[i].yOffset,1); + glTexCoord2d(0,0); glVertex3f(0+gScreenTextures[i].xOffset, gScreenTextures[i].yOffset,1); + glEnd(); + } + glCopyTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,gScreenTextures[i].xOffset,gScreenTextures[i].yOffset,gScreenTextures[i].xSize,gScreenTextures[i].ySize,0); + } + glPopMatrix(); + glPopAttrib(); + glMatrixMode(GL_MODELVIEW); + gLastFrameBlur=true; + } + else + gLastFrameBlur=false; +} + +void RenderFrame(int screenUpdate) +{ + #ifdef __POLYCOUNT + gPolyCount=0; + #endif + //gClipEnable=false; + +// SetupAspect(kNormalFOVY+~gCameraEntity->velo*0.002*gGameInfo->arcade); + float fov=1.0; + if(gGameInfo->arcade==kGameModeArcade||gGameInfo->arcade==kGameModeTurbo) + fov+=~gCameraEntity->velo*0.0015*(gGameInfo->arcade+1); + if(fov>1.6)fov=1.6; + SetupAspect(fov); + + tVector3 clipPlanes[kNumClipPlanes]; + SetupClipPlanes(gCameraEntity,clipPlanes); + + SetupLighting(); + + SkyRender(); + FlaresDraw1(); + RenderVisWalls(); + + SortEntities(); + + RenderObjectPass(clipPlanes); //first pass: objects + + if(!gMapInfo->dontDrawRoad) + RoadRender(clipPlanes); + TracksRender(clipPlanes); + + if(gConfig->stencil)//second pass: shadows/light Cones + { + int stencil=4.0f*gConfig->gfxDynamics; + if(stencil<=0) + stencil=1; + if(gEnvironment->shadowEnable) + { + for(int i=1;i<=stencil;i++) + { + gStencilZoom=0.9+0.1*i/(float)(stencil); + RenderShadowPass(clipPlanes,true); + RenderStencilLayer(true,stencil); + } + gStencilZoom=1.0; + RenderShadowPass(clipPlanes,false); + RenderStencilLayer(true,1); + } + if(gEnvironment->spotLightEnable) + { + for(int i=1;i<=stencil*1.5;i++) + { + gStencilZoom=0.75+0.25*i/((float)stencil*1.5); + RenderLightConesPass(clipPlanes); + RenderStencilLayer(false,stencil); + } + } + } + DrawTransparentPolys(clipPlanes);//third pass: transparent stuff + RenderLightsPass(clipPlanes); //fourth pass: lights + + ParticlesDraw(); + + + SetupAspect(kNormalFOVY); + FlaresDraw2(); + + if(!gBackgroundReplay) + { + GameShowInfo(); + MotionBlur(); + if(screenUpdate) + ScreenBlit(); + } + gLastGraphFrame=gFrameCount; +} \ No newline at end of file diff --git a/source/renderframe.h b/source/renderframe.h new file mode 100755 index 0000000..1752720 --- /dev/null +++ b/source/renderframe.h @@ -0,0 +1,54 @@ +#ifndef __RENDERFRAME +#define __RENDERFRAME + +#include "vectors.h" + +enum{ + kClipBasePoint=0, + kClipFarPoint, + kClipXPlane, + kClipYPlane, + kClipRearPlane, + kClipRightPlane, + kClipLeftPlane, + kClipTopPlane, + kClipBotPlane, + kNumClipPlanes +}; + +#define kClipSaveDistance 0 + +//#define __POLYCOUNT + +#ifdef __POLYCOUNT +extern int gPolyCount; +#endif + +enum{ + kCameraChase, + kCameraChaseClose, + kCameraCockpitCarHidden, + kCameraLeftWheel, + kCameraLeftSide, + kCameraTripod, + kCameraTop, + kCameraNumModes, + kCameraFree, + kCameraCockpit +}; + +extern int gCameraMode,gCameraReverse,gClipEnable; +extern tMatrix3 gTransformDir; +extern tVector3 gTransformPos; + +#define ClipDistance() (320.0f+480.0f*gConfig->gfxDynamics) + +void SetupTranslation(tVector3 objPos,tMatrix3 objDir); +void SetupWorldTranslation(); +void SetupLighting(); +void RenderFrame(); +int ClipPoint(tVector3 *clipPlanes,tVector3 *point); +int ClipPointDistanced(tVector3 *clipPlanes,tVector3 *point,float distance); +void RenderFrame(int screenUpdate); + +#endif \ No newline at end of file diff --git a/source/roads.cpp b/source/roads.cpp new file mode 100644 index 0000000..abbdb44 --- /dev/null +++ b/source/roads.cpp @@ -0,0 +1,1122 @@ +//roads.cpp +//draws the road and detects collisions agains the road. + +#include +#include +#include +#include "entities.h" +#include "config.h" +#include "vectors.h" +#include "fileio.h" +#include "textures.h" +#include "gamemem.h" +#include "renderframe.h" +#include "roads.h" +#include "environment.h" +#include "gameinitexit.h" +#include "transparency.h" +#include "network.h" + + + +#include +#include "error.h" +#define kExtraLength 80 + +tRoadTypeList *gRoadTypes; +tSurfaceTypeList *gSurfaceTypes; +int gRoadRestrictedBorders=false; +int gQuickRoadCollision=false; + +#define kMaxAILateralAcceleration 4.0 +#define kMaxAITurboLateralAcceleration 16.0 +#define kMaxAILongitunalAcceleration 4.8 +#define kMaxAITurboLongitunalAcceleration 13.5 +#define kMaxSpeedIndex 100 + +tVector2 GetFlatTrack(tConvertedRoad *convRoad,int i,int roadSize) +{ + if(i<0)i=0; + if(i>roadSize-2)i=roadSize-2; + tVector3 pos_3=convRoad->road[i].trackl+(convRoad->road[i].trackr-convRoad->road[i].trackl)*((convRoad->road[i].track+1)*0.5); + return Vector(pos_3.x,pos_3.z); +} + +#define kMinSignLateralAccel 2.5 +#define kSignDistance 100.0 + +void CalcSpeedInfo(int ref) +{ + tRoadData *roadData=(tRoadData*)FileGetDataPtr(ref); + tRoadSeg *road=roadData->road; + tConvertedRoad *convRoad=(tConvertedRoad*)gFileTable[ref].parsedData; + + if(roadData->roadSize<10) + return; + + + for(int i=2;i<(roadData->roadSize)-2;i++) + { + float traction=gRoadTypes->types[road[i].type].traction; + if(traction==0)traction=1; + tVector2 pos1=GetFlatTrack(convRoad,i-7,(roadData->roadSize)); + tVector2 pos2=GetFlatTrack(convRoad,i,(roadData->roadSize)); + tVector2 pos3=GetFlatTrack(convRoad,i+7,(roadData->roadSize)); + tVector2 v1=!(pos2-pos1); + tVector2 v2=!(pos3-pos2); + float veloDiff=~(v2-v1); + float dist=~(pos1-pos2)+~(pos2-pos3); + + if(dist>0&&veloDiff>0) + { + float accel=dist/veloDiff; + convRoad->road[i].speedIndex=sqrt(kMaxAILateralAcceleration*accel*traction); + if(convRoad->road[i].speedIndex>kMaxSpeedIndex) + convRoad->road[i].speedIndex=kMaxSpeedIndex; + convRoad->road[i].turboSpeedIndex=sqrt(kMaxAITurboLateralAcceleration*accel*traction); + if(convRoad->road[i].turboSpeedIndex>kMaxSpeedIndex) + convRoad->road[i].turboSpeedIndex=kMaxSpeedIndex; + } + else + { + convRoad->road[i].speedIndex=kMaxSpeedIndex; + convRoad->road[i].turboSpeedIndex=kMaxSpeedIndex; + } + + convRoad->road[i].lateralAccel=(v1.y*v2.x-v2.y*v1.x)/dist*1000; + if(road[i].speedModifier>=-1&&road[i].speedModifier<=1) + { + convRoad->road[i].speedIndex*=(1+road[i].speedModifier); + convRoad->road[i].turboSpeedIndex*=(1+road[i].speedModifier); + } + convRoad->road[i].reverseSpeedIndex=convRoad->road[i].speedIndex; + convRoad->road[i].reverseTurboSpeedIndex=convRoad->road[i].turboSpeedIndex; + } + convRoad->road[0].speedIndex=kMaxSpeedIndex; + convRoad->road[1].speedIndex=kMaxSpeedIndex; + convRoad->road[(roadData->roadSize)-1].speedIndex=kMaxSpeedIndex; + convRoad->road[(roadData->roadSize)-2].speedIndex=kMaxSpeedIndex; + convRoad->road[0].reverseSpeedIndex=kMaxSpeedIndex; + convRoad->road[1].reverseSpeedIndex=kMaxSpeedIndex; + convRoad->road[(roadData->roadSize)-1].reverseSpeedIndex=kMaxSpeedIndex; + convRoad->road[(roadData->roadSize)-2].reverseSpeedIndex=kMaxSpeedIndex; + + convRoad->road[0].turboSpeedIndex=kMaxSpeedIndex; + convRoad->road[1].turboSpeedIndex=kMaxSpeedIndex; + convRoad->road[(roadData->roadSize)-1].turboSpeedIndex=kMaxSpeedIndex; + convRoad->road[(roadData->roadSize)-2].turboSpeedIndex=kMaxSpeedIndex; + convRoad->road[0].reverseTurboSpeedIndex=kMaxSpeedIndex; + convRoad->road[1].reverseTurboSpeedIndex=kMaxSpeedIndex; + convRoad->road[(roadData->roadSize)-1].reverseTurboSpeedIndex=kMaxSpeedIndex; + convRoad->road[(roadData->roadSize)-2].reverseTurboSpeedIndex=kMaxSpeedIndex; + + + + for(int i=(roadData->roadSize)-2;i>=0;i--) + { + float traction=gRoadTypes->types[road[i].type].traction; + if(traction==0)traction=1; + tVector2 pos1=Vector(road[i].pos.x,road[i].pos.z); + tVector2 pos2=Vector(road[i+1].pos.x,road[i+1].pos.z); + float dist=~(pos1-pos2); + float speedIndex2=convRoad->road[i+1].speedIndex; + float maxSpeedIndex1=sqrt(sqr(speedIndex2)+kMaxAILongitunalAcceleration*2*dist*traction); + float maxTurboSpeedIndex1=sqrt(sqr(speedIndex2)+kMaxAITurboLongitunalAcceleration*2*dist*traction); + if(convRoad->road[i].speedIndex>maxSpeedIndex1) + convRoad->road[i].speedIndex=maxSpeedIndex1; + if(convRoad->road[i].turboSpeedIndex>maxTurboSpeedIndex1) + convRoad->road[i].turboSpeedIndex=maxTurboSpeedIndex1; + } + for(int i=1;i<(roadData->roadSize)-2;i++) + { + float traction=gRoadTypes->types[road[i].type].traction; + if(traction==0)traction=1; + tVector2 pos1=Vector(road[i].pos.x,road[i].pos.z); + tVector2 pos2=Vector(road[i-1].pos.x,road[i-1].pos.z); + float dist=~(pos1-pos2); + float speedIndex2=convRoad->road[i-1].reverseSpeedIndex; + float maxSpeedIndex1=sqrt(sqr(speedIndex2)+kMaxAILongitunalAcceleration*2*dist*traction); + float maxTurboSpeedIndex1=sqrt(sqr(speedIndex2)+kMaxAITurboLongitunalAcceleration*2*dist*traction); + if(convRoad->road[i].reverseSpeedIndex>maxSpeedIndex1) + convRoad->road[i].reverseSpeedIndex=maxSpeedIndex1; + if(convRoad->road[i].reverseTurboSpeedIndex>maxTurboSpeedIndex1) + convRoad->road[i].reverseTurboSpeedIndex=maxTurboSpeedIndex1; + } + + int cornerStart=-1; + float corner=0; + float iminus2=convRoad->road[0].lateralAccel; + float iminus1=convRoad->road[1].lateralAccel; + //smooth lateralAccel + for(int i=2;i<(roadData->roadSize)-3;i++) + { + float temp=convRoad->road[i].lateralAccel; + convRoad->road[i].lateralAccel= + (convRoad->road[i].lateralAccel+ + convRoad->road[i+1].lateralAccel+ + convRoad->road[i+2].lateralAccel+ + iminus1+iminus2)/5; + iminus2=iminus1; + iminus1=temp; + } + + for(int i=1;i<(roadData->roadSize)-2;i++) + { + if((convRoad->road[i].lateralAccel>kMinSignLateralAccel&&corner>=0)|| + (convRoad->road[i].lateralAccel>1.5&&corner>0)) + { + if(corner==0) + cornerStart=i; + if(convRoad->road[i].lateralAccel>corner) + corner=convRoad->road[i].lateralAccel; + } + else if((convRoad->road[i].lateralAccel<-kMinSignLateralAccel&&corner<=0)|| + (convRoad->road[i].lateralAccel<-1.5&&corner<0)) + { + if(corner==0) + cornerStart=i; + if(convRoad->road[i].lateralAccelroad[i].lateralAccel; + } + else if(corner) + { + for(int j=cornerStart;jroad[j].lateralAccel=corner; + convRoad->road[i].lateralAccel=0; + corner=0; + cornerStart=-1; + } + else + convRoad->road[i].lateralAccel=0; + } + if(corner) + { + for(int j=cornerStart;j<(roadData->roadSize)-3;j++) + convRoad->road[j].lateralAccel=corner; + } +} + +float Interpolation(float x) +{ + return -x*x*x*x+2*x*x; +} + +float InterpolateTrack(float l1, float l2, float l, float track1, float track2) +{ + return track1+(track2-track1)*Interpolation((l-l1)/(l2-l1)); +} + +void ConvertRoadEndianess(tRoadData *roadData) +{ + tRoadSeg *road=roadData->road; + S32Swap(roadData->roadSize); + + for(int i=0;iroadSize;i++) + { + F32Swap(road[i].pos.x); + F32Swap(road[i].pos.y); + F32Swap(road[i].pos.z); + F32Swap(road[i].normal.x); + F32Swap(road[i].normal.y); + F32Swap(road[i].normal.z); + S32Swap(road[i].type); + F32Swap(road[i].track); + F32Swap(road[i].typeSeg); + F32Swap(road[i].speedModifier); + } +} + + +void CompileRoadPoints(int id) +{ + tRoadData *roadData=(tRoadData*)FileGetDataPtr(id); + tRoadSeg *road=roadData->road; + + ConvertRoadEndianess(roadData); + + tConvertedRoad *convRoad=(tConvertedRoad*)MemoryAllocateBlock(((roadData->roadSize)-1)*sizeof(tConvertedRoadSeg)+sizeof(tConvertedRoad)); + + int vertexCount=0; + for(int i=0;i<(roadData->roadSize)-1;i++) + vertexCount+=gRoadTypes->types[(road[i].type)].numVertices+2; + + convRoad->vertexStorage=(tConvertedRoadVertex*)MemoryAllocateBlock(sizeof(tConvertedRoadVertex)*vertexCount); + + tVector3 prevSegDir,prevJunction2; + tVector3 curSegDir,curJunction1,curJunction2; + tVector3 nextSegDir,nextJunction1,nextJunction2; + tVector3 junction1,junction2; + + float totalLength=0; + float startTypeSeg=0; + vertexCount=0; + + curSegDir=road[1].pos-road[0].pos; + curJunction1=!(curSegDir%road[0].normal); + curJunction2=!(curSegDir%road[1].normal); + prevSegDir=curSegDir; + prevJunction2=curJunction1; + junction1=!(curJunction1+prevJunction2); + + if(road[0].track>1)road[0].track=0; + float lastTrackLength=0,lastTrack=road[0].track; + float nextTrackLength=0,nextTrack=road[0].track; + int lastTrackIndex=0,nextTrackIndex=0; + + for(int i=0;i<(roadData->roadSize)-1;i++) + { + if(nextTrackIndex<=i) + { + float l=totalLength; + for(int j=i+1;j<(roadData->roadSize)&&nextTrackIndex<=i;j++) + { + l+=~(road[j].pos-road[j-1].pos); + if(road[j].track<=1||j==(roadData->roadSize)-1) + { + lastTrackLength=nextTrackLength; + lastTrack=nextTrack; + lastTrackIndex=nextTrackIndex; + nextTrackIndex=j; + nextTrackLength=l; + nextTrack=(j==(roadData->roadSize)-1?road[0].track:road[nextTrackIndex].track); + } + } + } + + nextSegDir=i<(roadData->roadSize)-2?road[i+2].pos-road[i+1].pos:curSegDir; + nextJunction1=!(nextSegDir%road[i+1].normal); + nextJunction2=i<(roadData->roadSize)-2?!(nextSegDir%road[i+2].normal):nextJunction1; + + junction2=!(nextJunction1+curJunction1); + + convRoad->road[i].vertices=convRoad->vertexStorage+vertexCount+1; + convRoad->road[i].maxExtends=0; + + int numVertices=gRoadTypes->types[(road[i].type)].numVertices; + int startTopVertices=gRoadTypes->types[(road[i].type)].startTopVertices; + int endTopVertices=gRoadTypes->types[(road[i].type)].endTopVertices; + + vertexCount+=numVertices+2; + + convRoad->road[i].track=InterpolateTrack(lastTrackLength,nextTrackLength,totalLength,lastTrack,nextTrack); + convRoad->road[i].trackl=road[i].pos+junction1*gRoadTypes->types[(road[i].type)].minTrack; + convRoad->road[i].trackr=road[i].pos+junction1*gRoadTypes->types[(road[i].type)].maxTrack; + + for(int v=-1;vtypes[(road[i].type)].vertices[startTopVertices].vertex1+(gRoadTypes->types[(road[i].type)].vertices[startTopVertices].vertex2-gRoadTypes->types[(road[i].type)].vertices[startTopVertices].vertex1)*startTypeSeg; + typePoint2=gRoadTypes->types[(road[i].type)].vertices[startTopVertices].vertex1+(gRoadTypes->types[(road[i].type)].vertices[startTopVertices].vertex2-gRoadTypes->types[(road[i].type)].vertices[startTopVertices].vertex1)*road[i].typeSeg; + typePoint1.x-=3; + typePoint2.x-=3; + } + else if(v==numVertices) + { + typePoint1=gRoadTypes->types[(road[i].type)].vertices[endTopVertices-1].vertex1+(gRoadTypes->types[(road[i].type)].vertices[endTopVertices-1].vertex2-gRoadTypes->types[(road[i].type)].vertices[endTopVertices-1].vertex1)*startTypeSeg; + typePoint2=gRoadTypes->types[(road[i].type)].vertices[endTopVertices-1].vertex1+(gRoadTypes->types[(road[i].type)].vertices[endTopVertices-1].vertex2-gRoadTypes->types[(road[i].type)].vertices[endTopVertices-1].vertex1)*road[i].typeSeg; + typePoint1.x+=3; + typePoint2.x+=3; + } + else + { + typePoint1=gRoadTypes->types[(road[i].type)].vertices[v].vertex1+(gRoadTypes->types[(road[i].type)].vertices[v].vertex2-gRoadTypes->types[(road[i].type)].vertices[v].vertex1)*startTypeSeg; + typePoint2=gRoadTypes->types[(road[i].type)].vertices[v].vertex1+(gRoadTypes->types[(road[i].type)].vertices[v].vertex2-gRoadTypes->types[(road[i].type)].vertices[v].vertex1)*road[i].typeSeg; + } + + if(sqr(typePoint1)>convRoad->road[i].maxExtends) + convRoad->road[i].maxExtends=sqr(typePoint1); + if(sqr(typePoint2)>convRoad->road[i].maxExtends) + convRoad->road[i].maxExtends=sqr(typePoint2); + convRoad->road[i].vertices[v].v1=road[i].pos+road[i].normal*typePoint1.y+junction1*typePoint1.x; + convRoad->road[i].vertices[v].v2=road[i+1].pos+road[i+1].normal*typePoint2.y+junction2*typePoint2.x; + + convRoad->road[i].vertices[v].d2=convRoad->road[i].vertices[v].v2.y-convRoad->road[i].vertices[v].v1.y; + convRoad->road[i].vertices[v].d1=convRoad->road[i].vertices[v].v2.y-convRoad->road[i].vertices[v].v1.y; + if(i>0) + if((road[i-1].type)==(road[i].type)) + { + tVector3 v1=convRoad->road[i].vertices[v].v2-convRoad->road[i].vertices[v].v1; + tVector3 v2=convRoad->road[i].vertices[v].v1-convRoad->road[i-1].vertices[v].v1; + float l1=~v1; + float l2=~v2; + tVector3 v12=!(!v1+!v2); + float d=v12.y/sqrt(sqr(v12.x)+sqr(v12.z)); + if(fabs(convRoad->road[i].vertices[v].d1-d*l1)<2) + { + convRoad->road[i].vertices[v].d1=d*l1; + convRoad->road[i-1].vertices[v].d2=d*l2; + } + } + } + + startTypeSeg=road[i].typeSeg; + if(startTypeSeg>=1)startTypeSeg=0; + for(int v=-1;vroad[i].vertices[v].n1=!(junction1); + convRoad->road[i].vertices[v].n2=!(junction2); + } + else if(v==numVertices-1) + { + convRoad->road[i].vertices[v].n1=!(-junction1); + convRoad->road[i].vertices[v].n2=!(-junction2); + } + else + { + tVector2 typeNormal1=(gRoadTypes->types[(road[i].type)].vertices[v].vertex1-gRoadTypes->types[(road[i].type)].vertices[v+1].vertex1); + tVector2 typeNormal2=(gRoadTypes->types[(road[i].type)].vertices[v].vertex2-gRoadTypes->types[(road[i].type)].vertices[v+1].vertex2); + if(VectorEqual(typeNormal1,Vector(0,0))) + typeNormal1=Vector(-1,0); + if(VectorEqual(typeNormal2,Vector(0,0))) + typeNormal2=Vector(-1,0); + typeNormal1=!typeNormal1; + typeNormal2=!typeNormal2; + + convRoad->road[i].vertices[v].n1=!(-road[i].normal*typeNormal1.x+junction1*typeNormal1.y); + convRoad->road[i].vertices[v].n2=!(-road[i+1].normal*typeNormal2.x+junction2*typeNormal2.y); + } + } + + convRoad->road[i].length=totalLength; + totalLength+=~curSegDir; + + convRoad->road[i].maxExtends=sqrt(convRoad->road[i].maxExtends); + + prevJunction2=curJunction2; + curSegDir=nextSegDir; + curJunction1=nextJunction1; + curJunction2=nextJunction2; + junction1=junction2; + } + gFileTable[id].parsedData=convRoad; + gFileTable[id].parsed=true; + CalcSpeedInfo(id); +} + + +//Renders the Road +void RoadRender(tVector3 *clipPlanes) +{ + tRoadData *roadData=(tRoadData*)FileGetDataPtr(gMapInfo->road); + tRoadSeg *road=roadData->road; + if(!gFileTable[gMapInfo->road].parsed) + CompileRoadPoints(gMapInfo->road); + if(!gFileTable[gMapInfo->road].parsed) + return; + tConvertedRoad *convRoad=(tConvertedRoad*)gFileTable[gMapInfo->road].parsedData; + + SetupWorldTranslation(); + + glPushAttrib(GL_LIGHTING_BIT+GL_COLOR_BUFFER_BIT+GL_POLYGON_BIT); + +/* GLfloat specularReflectance[]={0.0,0.0,0.0,1}; + GLfloat ambientReflectance[]={0.6,0.6,0.6,1}; + GLfloat diffuseReflectance[]={0.4,0.4,0.4,1}; +*/ + GLfloat specularReflectance[]={0.0,0.0,0.0,1}; + GLfloat ambientReflectance[]={0.4,0.4,0.4,1}; + GLfloat diffuseReflectance[]={0.6,0.6,0.6,1}; + + glMaterialfv(GL_FRONT,GL_DIFFUSE,diffuseReflectance); + glMaterialfv(GL_FRONT,GL_AMBIENT,ambientReflectance); + glMaterialfv(GL_FRONT,GL_SPECULAR,specularReflectance); + glMaterialf(GL_FRONT,GL_SHININESS,20); + + int lastClipped=0; + int texture=-1; + + tPolyMaterial mat; + mat.flags=kMaterialBothSides+kMaterialUseAlphaChannel+kMaterialDisableWrapS; + mat.shininess=0; + mat.specular=Vector(0,0,0); + mat.ambient=Vector(0.6,0.6,0.6); + mat.diffuse=Vector(0.4,0.4,0.4); + + for(int i=0;i<(roadData->roadSize)-2;i++) + { + float len1=convRoad->road[i].length; + float len2=convRoad->road[i+1].length; + int clipped=ClipPointDistanced(clipPlanes,&road[i+1].pos,convRoad->road[i].maxExtends); + + if(!(clipped&lastClipped)) + { + int numVertices=gRoadTypes->types[(road[i].type)].numVertices; + for(int v=0;vtypes[(road[i].type)].vertices[v].texZoom; + if(texZoom>=0) + { + if(!(gRoadTypes->types[(road[i].type)].vertices[v].materialFlags&1)) + { + if(gRoadTypes->types[(road[i].type)].vertices[v].materialFlags&2) + glDisable(GL_CULL_FACE); + else + glEnable(GL_CULL_FACE); + + if(texture!=gRoadTypes->types[(road[i].type)].vertices[v].texture) + { + texture=gRoadTypes->types[(road[i].type)].vertices[v].texture; + TexturesSelectTex(texture); + if(!gConfig->carsOnSpeed) + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); + } + + glBegin(GL_TRIANGLE_STRIP); + + glNormal3fv(&convRoad->road[i].vertices[v].n2.x); + glTexCoord2f(gRoadTypes->types[(road[i].type)].vertices[v+1].texCoord1,texZoom?convRoad->road[i+1].length*texZoom:1); + glVertex3fv(&convRoad->road[i].vertices[v+1].v2.x); + + glNormal3fv(&convRoad->road[i].vertices[v].n1.x); + glTexCoord2f(gRoadTypes->types[(road[i].type)].vertices[v].texCoord2,texZoom?convRoad->road[i+1].length*texZoom:1); + glVertex3fv(&convRoad->road[i].vertices[v].v2.x); + + glNormal3fv(&convRoad->road[i].vertices[v].n2.x); + glTexCoord2f(gRoadTypes->types[(road[i].type)].vertices[v+1].texCoord1,texZoom?convRoad->road[i].length*texZoom:0); + glVertex3fv(&convRoad->road[i].vertices[v+1].v1.x); + + glNormal3fv(&convRoad->road[i].vertices[v].n1.x); + glTexCoord2f(gRoadTypes->types[(road[i].type)].vertices[v].texCoord2,texZoom?convRoad->road[i].length*texZoom:0); + glVertex3fv(&convRoad->road[i].vertices[v].v1.x); + + glEnd(); + /* + glBegin(GL_LINES); + glVertex3fv(&convRoad->road[i].vertices[v+1].v2.x); + glVertex3fv(&(convRoad->road[i].vertices[v+1].v2+convRoad->road[i].vertices[v].n2).x); + glVertex3fv(&convRoad->road[i].vertices[v+1].v1.x); + glVertex3fv(&(convRoad->road[i].vertices[v+1].v1+convRoad->road[i].vertices[v].n1*2).x); + glVertex3fv(&convRoad->road[i].vertices[v].v2.x); + glVertex3fv(&(convRoad->road[i].vertices[v].v2+convRoad->road[i].vertices[v].n2).x); + glVertex3fv(&convRoad->road[i].vertices[v].v1.x); + glVertex3fv(&(convRoad->road[i].vertices[v].v1+convRoad->road[i].vertices[v].n1*2).x); + glEnd();*/ + } + else if(tTransparentPoly *p=GetNextTransparentPoly(true)) + { + mat.texRef=gRoadTypes->types[(road[i].type)].vertices[v].texture; + + *p->mat=mat; + p->textureSet=0; + p->v[0].vertex=convRoad->road[i].vertices[v+1].v2; + p->v[1].vertex=convRoad->road[i].vertices[v].v2; + p->v[2].vertex=convRoad->road[i].vertices[v+1].v1; + p->v[0].normal=convRoad->road[i].vertices[v].n2; + p->v[1].normal=convRoad->road[i].vertices[v].n1; + p->v[2].normal=convRoad->road[i].vertices[v].n2; + p->v[0].texel=Vector(gRoadTypes->types[(road[i].type)].vertices[v+1].texCoord1,texZoom?convRoad->road[i+1].length*texZoom:1); + p->v[1].texel=Vector(gRoadTypes->types[(road[i].type)].vertices[v].texCoord2,texZoom?convRoad->road[i+1].length*texZoom:1); + p->v[2].texel=Vector(gRoadTypes->types[(road[i].type)].vertices[v+1].texCoord1,texZoom?convRoad->road[i].length*texZoom:0); + + if(tTransparentPoly *p=GetNextTransparentPoly(true)) + { + *p->mat=mat; + p->textureSet=0; + p->v[0].vertex=convRoad->road[i].vertices[v].v1; + p->v[1].vertex=convRoad->road[i].vertices[v].v2; + p->v[2].vertex=convRoad->road[i].vertices[v+1].v1; + p->v[0].normal=convRoad->road[i].vertices[v].n1; + p->v[1].normal=convRoad->road[i].vertices[v].n1; + p->v[2].normal=convRoad->road[i].vertices[v].n2; + p->v[0].texel=Vector(gRoadTypes->types[(road[i].type)].vertices[v].texCoord2,texZoom?convRoad->road[i].length*texZoom:0); + p->v[1].texel=Vector(gRoadTypes->types[(road[i].type)].vertices[v].texCoord2,texZoom?convRoad->road[i+1].length*texZoom:1); + p->v[2].texel=Vector(gRoadTypes->types[(road[i].type)].vertices[v+1].texCoord1,texZoom?convRoad->road[i].length*texZoom:0); + } + } + #ifdef __POLYCOUNT + gPolyCount+=2; + #endif + } + } + + } + lastClipped=clipped; + } + + glPopAttrib(); +} + + +tVector2 OrtoVec(tVector2 v) +{ + return Vector(v.y,-v.x); +} + +float GetPlaneHeight(tVector3 pos,tVector3 planeNormal,tVector3 planePoint) +{ + return -(planeNormal.x*pos.x+planeNormal.z*pos.z-planeNormal*planePoint)/planeNormal.y; +} + +int RoadGetRoadSegment(tVector3 pos,int lastSegment) +{ + tRoadData *roadData=(tRoadData*)FileGetDataPtr(gMapInfo->road); + tRoadSeg *road=roadData->road; + if(!gFileTable[gMapInfo->road].parsed) + CompileRoadPoints(gMapInfo->road); + if(!gFileTable[gMapInfo->road].parsed) + return 0; + tConvertedRoad *convRoad=(tConvertedRoad*)gFileTable[gMapInfo->road].parsedData; + + tVector2 flatPos=Vector(pos.x,pos.z); + + int max=(gRoadRestrictedBorders&&lastSegment)?15:(roadData->roadSize)*2; + + //a hack to always get a result for the camera, which jumps around a lot. + //if((lastSegment==gCameraEntity->lastRoadIndex)||gReplay||lastSegment==0) + // max=(roadData->roadSize)*2; + + for(int ind=1;indroadSize)-2; + while(i>=(roadData->roadSize)-2)i-=(roadData->roadSize)-2; + if(sqr(road[i].pos.x-flatPos.x)+sqr(road[i].pos.z-flatPos.y)road[i+1].length-convRoad->road[i].length+kExtraLength)) + { + int numVertices=gRoadTypes->types[(road[i].type)].numVertices; +// int endTopVertices=gRoadTypes->types[(road[i].type)].endTopVertices; +// int startTopVertices=gRoadTypes->types[(road[i].type)].startTopVertices; + int endTopVertices=gRoadTypes->types[(road[i].type)].numVertices+1; + int startTopVertices=-1; + tVector2 right1,left1,right2,left2; + tVector2 rDir=Vector(road[i+1].pos.x-road[i].pos.x,road[i+1].pos.z-road[i].pos.z); + do{ + right1=Vector(convRoad->road[i].vertices[startTopVertices].v1.x, convRoad->road[i].vertices[startTopVertices].v1.z ); + right2=Vector(convRoad->road[i].vertices[startTopVertices].v2.x, convRoad->road[i].vertices[startTopVertices].v2.z ); + startTopVertices++; + }while((right2-right1)*rDir<0); + do{ + left1= Vector(convRoad->road[i].vertices[endTopVertices-1].v1.x, convRoad->road[i].vertices[endTopVertices-1].v1.z ); + left2= Vector(convRoad->road[i].vertices[endTopVertices-1].v2.x, convRoad->road[i].vertices[endTopVertices-1].v2.z ); + endTopVertices--; + }while((left2-left1)*rDir<0); + if((right1-flatPos) *!OrtoVec(left1-right1)<=0.5) + if((left2-flatPos) *OrtoVec(right2-left2)<=0) + if((right1-flatPos) *!OrtoVec(right1-right2)<=0.5) + if((left2-flatPos) *OrtoVec(left2-left1)<=0) + return i; + } + } + return -1; +} + +float RoadGetPosition(tVector3 pos,int lastSegment,float *track) +{ + int i=RoadGetRoadSegment(pos,lastSegment); + if(i==-1)return 0; + + tRoadData *roadData=(tRoadData*)FileGetDataPtr(gMapInfo->road); + tRoadSeg *road=roadData->road; + tConvertedRoad *convRoad=(tConvertedRoad*)gFileTable[gMapInfo->road].parsedData; + + tVector3 point1=road[i].pos; + tVector3 point2=road[i+1].pos; + float localPosition=((pos-point1)*(point2-point1))/sqr(point2-point1); + if(track) + { + point1=(convRoad->road[i].trackl+convRoad->road[i+1].trackl)*0.5; + point2=(convRoad->road[i].trackr+convRoad->road[i+1].trackr)*0.5; + *track=((pos-point1)*(point2-point1))/sqr(point2-point1); + } + return localPosition+i; +} + +float RoadGetLength(tVector3 pos,int lastSegment) +{ + float f=RoadGetPosition(pos,lastSegment,NULL); + int i=floor(f); + tRoadData *roadData=(tRoadData*)FileGetDataPtr(gMapInfo->road); + tConvertedRoad *convRoad=(tConvertedRoad*)gFileTable[gMapInfo->road].parsedData; + if(i<0)i=0; + if(iroadSize-1) + return convRoad->road[i].length+(convRoad->road[i+1].length-convRoad->road[i].length)*(f-i); + else + return convRoad->road[i].length; +} + +void RoadInitCheckPoints(tVector3 startPos) +{ + tRoadData *roadData=(tRoadData*)FileGetDataPtr(gMapInfo->road); + float pos=RoadGetPosition(startPos,0,NULL); + float stepSize=((roadData->roadSize)-3)/kNumCheckPoints; + for(int i=0;ireverse?-stepSize:stepSize; + if(pos>((roadData->roadSize)-3)) + pos-=((roadData->roadSize)-3); + if(pos<0) + pos+=(roadData->roadSize)-3; + + for(int j=0;jroad); + float pos1=RoadGetPosition(startPos,0,NULL); + float pos2=RoadGetPosition(endPos,0,NULL); +/* if(gGameInfo->reverse) + { + float tmp=pos2; + pos2=pos1; + pos1=tmp; + }*/ + float pos=pos1; + float stepSize=(pos2-pos1)/kNumCheckPoints; + + for(int i=0;iroad); + tConvertedRoad *convRoad=(tConvertedRoad*)gFileTable[gMapInfo->road].parsedData; + + float l=convRoad->road[initPos].length+length; + int i=initPos; + + if(l<0) + l+=convRoad->road[(roadData->roadSize)-2].length; + if(l>convRoad->road[(roadData->roadSize)-2].length) + l-=convRoad->road[(roadData->roadSize)-2].length; + + if(lroad[initPos].length) + while(i>0&&convRoad->road[i].length>l) + i--; + else + while(i<(roadData->roadSize)-1&&convRoad->road[i].lengthroad); + if(!gFileTable[gMapInfo->road].parsed) + CompileRoadPoints(gMapInfo->road); + if(!gFileTable[gMapInfo->road].parsed) + return; + tConvertedRoad *convRoad=(tConvertedRoad*)gFileTable[gMapInfo->road].parsedData; + gNumCornerSigns=0; + + float accel=0; + int lastCurvePos=0; + + if(gGameInfo->reverse) + for(int i=(roadData->roadSize)-3;i>0;i--) + { + if(convRoad->road[i].lateralAccel!=accel) + { + if(convRoad->road[i].lateralAccel!=0&&gNumCornerSignslastCurvePos) + gCornerSigns[gNumCornerSigns].pos=lastCurvePos; + gCornerSigns[gNumCornerSigns].accel=convRoad->road[i].lateralAccel; + gNumCornerSigns++; + } + accel=convRoad->road[i].lateralAccel; + lastCurvePos=i; + } + } + else for(int i=1;i<(roadData->roadSize)-2;i++) + { + if(convRoad->road[i].lateralAccel!=accel) + { + if(convRoad->road[i].lateralAccel!=0&&gNumCornerSignsroad[i].lateralAccel; + gNumCornerSigns++; + } + accel=convRoad->road[i].lateralAccel; + lastCurvePos=i; + } + } + +} + +tVector3 RoadGetDir(int segment) +{ + tRoadData *roadData=(tRoadData*)FileGetDataPtr(gMapInfo->road); + tRoadSeg *road=roadData->road; + + tVector3 point1=road[segment].pos; + tVector3 point2=road[segment+1].pos; + return !(gGameInfo->reverse?point1-point2:point2-point1); +} + +void RoadCenterCar(tGameEntity *carEntity) +{ + tRoadData *roadData=(tRoadData*)FileGetDataPtr(gMapInfo->road); + tRoadSeg *road=roadData->road; + tConvertedRoad *convRoad=(tConvertedRoad*)gFileTable[gMapInfo->road].parsedData; + + int i=RoadGetRoadSegment(carEntity->pos,carEntity->lastRoadIndex); + if(i==-1)return; + + float track; + float dropL=RoadGetLength(road[i].pos,i); + int trackChoose=0; + int ok; + do{ + switch(trackChoose) + { + case 0:track=0.9;break; + case 1:track=0.1;break; + case 2:track=0.5;break; + case 3:track=0.7;break; + case 4:track=0.3;break; + } + ok=true; + for(int car=0;carnumPlayers;car++) + if(gCarEntities[car]!=carEntity) + { + float l=RoadGetLength(gCarEntities[car]->pos,gCarEntities[car]->lastRoadIndex); + if(!gGameInfo->reverse) + { + if(dropL-l<4*~gCarEntities[car]->velo&&dropL-l>0) + { + float ctrack; + RoadGetPosition(gCarEntities[car]->pos,gCarEntities[car]->lastRoadIndex,&ctrack); + if(fabs(ctrack-track)<0.2) + ok=false; + } + } + else + if(l-dropL<4*~gCarEntities[car]->velo&&l-dropL>0) + { + float ctrack; + RoadGetPosition(gCarEntities[car]->pos,gCarEntities[car]->lastRoadIndex,&ctrack); + if(fabs(ctrack-track)<0.2) + ok=false; + } + + } + trackChoose++; + }while(!ok&&trackChoose<5); + + tVector3 point=convRoad->road[i].trackl+(convRoad->road[i].trackr-convRoad->road[i].trackl)*track; + tVector3 dir=RoadGetDir(i); + *MatrixGetZVector(carEntity->dir)=dir; + *MatrixGetYVector(carEntity->dir)=Vector(0,1,0); + MatrixReAdjust(carEntity->dir); + carEntity->pos=point+((carEntity->pos-point)*dir)*dir; + carEntity->pos.y+=2+gRoadTypes->types[(road[i].type)].reviveY; + carEntity->pos=carEntity->pos-*MatrixGetXVector(carEntity->dir)*gRoadTypes->types[(road[i].type)].reviveX; + carEntity->velo=Vector(0,0,0); + MatrixIdentity(carEntity->rVelo); +} + +tVector3 RoadGetNextWaypoint(tVector3 pos,int *lastRoadIndex,float *overtaking,float *speedIndex,float aheadDistance) +{ + *lastRoadIndex=RoadGetRoadSegment(pos,*lastRoadIndex); + float l=RoadGetLength(pos,*lastRoadIndex)+(gGameInfo->reverse?-aheadDistance:aheadDistance); + int i=*lastRoadIndex; + tRoadData *roadData=(tRoadData*)FileGetDataPtr(gMapInfo->road); + tConvertedRoad *convRoad=(tConvertedRoad*)gFileTable[gMapInfo->road].parsedData; + + if(l<0) + l+=convRoad->road[(roadData->roadSize)-2].length; + if(l>convRoad->road[(roadData->roadSize)-2].length) + l-=convRoad->road[(roadData->roadSize)-2].length; + + if(l0&&convRoad->road[i].length>l) + i--; + else + while(i<(roadData->roadSize)-1&&convRoad->road[i].lengthreverse) + if(gGameInfo->arcade==2) + *speedIndex=convRoad->road[(int)f].reverseTurboSpeedIndex+(convRoad->road[((int)f)+1].reverseTurboSpeedIndex-convRoad->road[(int)f].reverseTurboSpeedIndex)*(f-(int)f); + else + *speedIndex=convRoad->road[(int)f].reverseSpeedIndex+(convRoad->road[((int)f)+1].reverseSpeedIndex-convRoad->road[(int)f].reverseSpeedIndex)*(f-(int)f); + else + if(gGameInfo->arcade==2) + *speedIndex=convRoad->road[(int)f].turboSpeedIndex+(convRoad->road[((int)f)+1].turboSpeedIndex-convRoad->road[(int)f].turboSpeedIndex)*(f-(int)f); + else + *speedIndex=convRoad->road[(int)f].speedIndex+(convRoad->road[((int)f)+1].speedIndex-convRoad->road[(int)f].speedIndex)*(f-(int)f); + + if(i<=0)i=1; + f=(l-convRoad->road[i-1].length)/(convRoad->road[i].length-convRoad->road[i-1].length); + if(f<0)f=0; + if(f>1)f=1; + + float track1=convRoad->road[i-1].track+(*overtaking>0?(1-convRoad->road[i-1].track)**overtaking:(1+convRoad->road[i-1].track)**overtaking); + tVector3 trackPos1=convRoad->road[i-1].trackl+((convRoad->road[i-1].trackr-convRoad->road[i-1].trackl)*((track1+1)/2)); + float track2=convRoad->road[i].track+(*overtaking>0?(1-convRoad->road[i].track)**overtaking:(1+convRoad->road[i].track)**overtaking); + tVector3 trackPos2=convRoad->road[i].trackl+((convRoad->road[i].trackr-convRoad->road[i].trackl)*((track2+1)/2)); + *overtaking=convRoad->road[i].track; + + return trackPos1+f*(trackPos2-trackPos1)+Vector(0,gRoadTypes->types[(roadData->road[i].type)].reviveY,0); +} + + +float InterpolateSplineAt(float y0,float y1,float d0,float d1,float x) +{ + float a= 2*y0-2*y1+ d0+ d1; + float b=-3*y0+3*y1-2*d0- d1; + float c= d0 ; + float d= y0 ; + return a*x*x*x+b*x*x+c*x+d; +} + +float RoadGetYValue(tVector3 point,int *lastRoadIndex,tVector3 *groundNormal,int *surfaceType,float *bumpOut) +{ + tRoadData *roadData=(tRoadData*)FileGetDataPtr(gMapInfo->road); + tRoadSeg *road=roadData->road; + if(!gFileTable[gMapInfo->road].parsed) + CompileRoadPoints(gMapInfo->road); + if(!gFileTable[gMapInfo->road].parsed) + return -INFINITY; + tConvertedRoad *convRoad=(tConvertedRoad*)gFileTable[gMapInfo->road].parsedData; + + tVector2 flatPos=Vector(point.x,point.z); + + int i=RoadGetRoadSegment(point,*lastRoadIndex); + if(i!=-1) + { + *lastRoadIndex=i; + int numVertices=gRoadTypes->types[(road[i].type)].numVertices; + int endTopVertices=gRoadTypes->types[(road[i].type)].endTopVertices; + int startTopVertices=gRoadTypes->types[(road[i].type)].startTopVertices; +// int endTopVertices=gRoadTypes->types[(road[i].type)].numVertices+1; +// int startTopVertices=-1; + + unsigned int min=startTopVertices,max=endTopVertices-1; + + tVector2 min1v2=Vector(convRoad->road[i].vertices[min].v1.x, convRoad->road[i].vertices[min].v1.z ); + tVector2 min2v2=Vector(convRoad->road[i].vertices[min].v2.x, convRoad->road[i].vertices[min].v2.z ); + if((min1v2-flatPos)*OrtoVec(min1v2-min2v2)>=0) + { + if(surfaceType) + *surfaceType=gMapInfo->baseSurfaceType; + if(groundNormal) + *groundNormal=convRoad->road[i].vertices[-1].n1; + if(bumpOut) + *bumpOut=0; + return -(min1v2-flatPos)*!OrtoVec(min1v2-min2v2); + } + tVector2 max1v2=Vector(convRoad->road[i].vertices[max].v1.x, convRoad->road[i].vertices[max].v1.z ); + tVector2 max2v2=Vector(convRoad->road[i].vertices[max].v2.x, convRoad->road[i].vertices[max].v2.z ); + if((max1v2-flatPos)*OrtoVec(max1v2-max2v2)<=0) + { + if(surfaceType) + *surfaceType=gMapInfo->baseSurfaceType; + if(groundNormal) + *groundNormal=convRoad->road[i].vertices[gRoadTypes->types[(road[i].type)].numVertices-1].n1; +// *groundNormal=!(road[i].pos-point); + if(bumpOut) + *bumpOut=0; + //printf("max %f\n",(max1v2-flatPos)*!OrtoVec(max1v2-max2v2)); + return (max1v2-flatPos)*!OrtoVec(max1v2-max2v2); + } + + while(max>min+1) + { + unsigned int mid=(min+max)/2; + tVector2 mid1=Vector(convRoad->road[i].vertices[mid].v1.x, convRoad->road[i].vertices[mid].v1.z ); + tVector2 mid2=Vector(convRoad->road[i].vertices[mid].v2.x, convRoad->road[i].vertices[mid].v2.z ); + if((mid1-flatPos)*OrtoVec(mid1-mid2)>=0) + max=mid; + else + min=mid; + } + int surface; + if(min!=-1&&max!=endTopVertices-1) + surface=gRoadTypes->types[(road[i].type)].vertices[min].surfaceType; + else + surface=gMapInfo->baseSurfaceType; + if(surfaceType) + *surfaceType=surface; + tVector3 min2=convRoad->road[i].vertices[min].v2; + tVector3 max2=convRoad->road[i].vertices[max].v2; + tVector3 min1=convRoad->road[i].vertices[min].v1; + tVector3 max1=convRoad->road[i].vertices[max].v1; + + tVector2 flatMin1=Vector(min1.x,min1.z); + tVector2 flatMin2=Vector(min2.x,min2.z); + tVector2 flatMax1=Vector(max1.x,max1.z); + tVector2 flatMax2=Vector(max2.x,max2.z); + + float u,v; + tVector2 a,b,c; + if(((flatMin1-flatPos)*OrtoVec(flatMin1-flatMax2)<=0||sqr(flatMax1-flatMin1)==0)&&!(sqr(flatMin2-flatMax2)==0)) + { + a=flatMin2-flatMax2; + b=flatMax1-flatMax2; + c=flatPos-flatMax2; + } + else + { + a=flatMax1-flatMin1; + b=flatMin2-flatMin1; + c=(flatMin1+a+b)-flatPos; + } + + if(a.x!=0&&a.y!=0&&b.x!=0&&b.y!=0){ + if(a.x==0) + { + v=(c.x)/(b.x); + u=(c.y-v*b.y)/(a.y); + } + else if(a.y==0) + { + v=(c.y)/(b.y); + u=(c.x-v*b.x)/(a.x); + } + else + { + u=(b.x*c.y-b.y*c.x)/(a.y*b.x-a.x*b.y); + if(b.y==0) + { + v=(c*a)/sqr(a); + } + else + v=(c.y-u*a.y)/b.y; + } + + + + // float miny=InterpolateSplineAt(min1.y,min2.y,convRoad->road[i].vertices[min].d1,convRoad->road[i].vertices[min].d2,1-v); + // float maxy=InterpolateSplineAt(max1.y,max2.y,convRoad->road[i].vertices[max].d1,convRoad->road[i].vertices[max].d2,1-v); + float miny=min2.y-v*(min2.y-min1.y); + float maxy=max2.y-v*(max2.y-max1.y); + float y=maxy-u*(maxy-miny); + + if(groundNormal) + { + tVector3 vmin=min2-v*(min2-min1); + vmin.y=miny; + tVector3 vmax=max2-v*(max2-max1); + vmax.y=maxy; + tVector3 v1=max1-u*(max1-min1); + tVector3 v2=max2-u*(max2-min2); + *groundNormal=!((vmin-vmax)%(v1-v2)); + } + + float bumpFreq=gSurfaceTypes->types[surface].bumpFreq; + float bump=gSurfaceTypes->types[surface].bumpHeight*sin(flatPos.x*bumpFreq)*sin(flatPos.y*bumpFreq); + if(bumpOut)*bumpOut=bump; + return point.y-y-bump; + } + else + { + tVector3 normal=!((min1-max2)%(max1-min2)); + + if(groundNormal) + *groundNormal=normal; + float bumpFreq=gSurfaceTypes->types[surface].bumpFreq; + float bump=gSurfaceTypes->types[surface].bumpHeight*sin(flatPos.x*bumpFreq)*sin(flatPos.y*bumpFreq); + if(bumpOut)*bumpOut=bump; + return point.y-GetPlaneHeight(point,normal,min2)-bump; + } + } + + if(!gQuickRoadCollision) + { + int closestRoadSeg=-1; + float closestRoadSegDist=INFINITY; + tVector2 normal; + for(int i=0;i<(roadData->roadSize)-2;i++) + { + if(sqr(road[i].pos.x-point.x)+sqr(road[i].pos.z-point.z)road[i+1].length-convRoad->road[i].length+kExtraLength)) + { + int numVertices=gRoadTypes->types[(road[i].type)].numVertices; + int endTopVertices=gRoadTypes->types[(road[i].type)].endTopVertices; + int startTopVertices=gRoadTypes->types[(road[i].type)].startTopVertices; + tVector2 right1=Vector(convRoad->road[i].vertices[startTopVertices].v1.x, convRoad->road[i].vertices[startTopVertices].v1.z ); + tVector2 left1= Vector(convRoad->road[i].vertices[endTopVertices-1].v1.x, convRoad->road[i].vertices[endTopVertices-1].v1.z ); + tVector2 right2=Vector(convRoad->road[i].vertices[startTopVertices].v2.x, convRoad->road[i].vertices[startTopVertices].v2.z ); + tVector2 left2= Vector(convRoad->road[i].vertices[endTopVertices-1].v2.x, convRoad->road[i].vertices[endTopVertices-1].v2.z ); + float dist; + tVector2 testNormal; + if(((right1-flatPos)*!OrtoVec(right1-left1))>=0) + if(((left2-flatPos)*!OrtoVec(right2-left2))<=0) + { + if((dist=(right1-flatPos)*(testNormal=!OrtoVec(right1-right2)))<=closestRoadSegDist) + if(dist>=0) + { + closestRoadSeg=i; + closestRoadSegDist=dist; + normal=testNormal; + } + if((dist=(left2-flatPos)*(testNormal=!OrtoVec(left2-left1)))<=closestRoadSegDist) + if(dist>=0) + { + closestRoadSeg=i; + closestRoadSegDist=dist; + normal=testNormal; + } + } + } + } + if(closestRoadSeg!=-1) + { + if(surfaceType) + *surfaceType=gMapInfo->baseSurfaceType; + if(groundNormal) + *groundNormal=Vector(normal.x,0,normal.y); + return -closestRoadSegDist; + } + } + if(surfaceType) + *surfaceType=gMapInfo->baseSurfaceType; + if(groundNormal) + *groundNormal=Vector(0,1,0); + return 0; +} + +void RoadGetWayPointData(int startRoadSeg,float dist,float *minTrack,float *maxTrack,float *minSpeed) +{ + tRoadData *roadData=(tRoadData*)FileGetDataPtr(gMapInfo->road); + tRoadSeg *road=roadData->road; + if(!gFileTable[gMapInfo->road].parsed) + CompileRoadPoints(gMapInfo->road); + if(!gFileTable[gMapInfo->road].parsed) + return; + tConvertedRoad *convRoad=(tConvertedRoad*)gFileTable[gMapInfo->road].parsedData; + float l=0; + *minTrack=1; + *maxTrack=-1; + *minSpeed=INFINITY; + while(lroad[startRoadSeg].track<*minTrack) + *minTrack=convRoad->road[startRoadSeg].track; + if(convRoad->road[startRoadSeg].track>*maxTrack) + *maxTrack=convRoad->road[startRoadSeg].track; + if(convRoad->road[startRoadSeg].speedIndex<*minSpeed) + *minSpeed=convRoad->road[startRoadSeg].speedIndex; + startRoadSeg++; + if(startRoadSeg>=(roadData->roadSize)-1) + startRoadSeg=0; + else + l+=convRoad->road[startRoadSeg].length-convRoad->road[startRoadSeg-1].length; + } +} diff --git a/source/roads.h b/source/roads.h new file mode 100755 index 0000000..edf06a1 --- /dev/null +++ b/source/roads.h @@ -0,0 +1,115 @@ +#ifndef __ROADS +#define __ROADS + +#include "vectors.h" +#include "entities.h" + +typedef struct{ + int texture; + int materialFlags; + int surfaceType; + tVector2 vertex1,vertex2; + float texCoord1,texCoord2,texZoom; +}tRoadTypeVertex; + +typedef struct{ + int endTopVertices,startTopVertices; + int startShadow,endShadow; + float minTrack,maxTrack; + float traction; + float reviveX,reviveY; + char name[80]; + int numVertices; + tRoadTypeVertex *vertices; +} tRoadType; + +typedef struct{ + int numTypes; + tRoadType *types; +} tRoadTypeList; + +typedef struct{ + float grip,slideGrip; + float bumpHeight,bumpFreq; + float brakeFactor; + int smokeEnable,trackEnable,soundEnable; + int soundEcho,sparksEnable,squeachEnable; + int reflectionEnable,smokeStickEnable; + float minSmokeSlideVelo,maxSmokeSlideVelo; + float minTrackSlideVelo,maxTrackSlideVelo; + float minSkidPitchSlideVelo,maxSkidPitchSlideVelo; + float minSkidGainSlideVelo,maxSkidGainSlideVelo; + int skidSound; + float smokeSize,smokeGravity,smokeSpeed,smokeSpread,smokeMaxLife,smokeMaxVelo; + int smokeTexture,trackTexture; +} tSurfaceType; + +typedef struct{ + int numTypes; + tSurfaceType *types; +} tSurfaceTypeList; + +//one segment of a road +typedef struct{ + tVector3 pos; //the point the road passes through + tVector3 normal; //a normal pointing upwards on the road surface + int type; //an index specifying the type of the road segment (bridge, tunnel, etc..) + float speedModifier; + int unused2; + float track; //the racing line the ai will follow: 1 is all to the right of the road and -1 all to the left + //any value >1 will be ignored. instead a value will be interpolated from other road points. + float typeSeg; //when using a road segment type which is not "symmetrical", ie. which looks different on + //one end than on the other, typeSeg can be used to "split" the type among several segments. + //one has to take a look at some road files to understand this properly. +} tRoadSeg; + +//the road +typedef struct{ + int roadSize; //number of road segments + tRoadSeg road[1]; //and the road data +} tRoadData; + +typedef struct{ + tVector3 v1,v2; + tVector3 n1,n2; + float d1,d2; +} tConvertedRoadVertex; + +//tRoadSeg structures get converted into tConvertedRoadSeg structures, which contain all the vertices +//of each road segment, so these don't need to be calculated from the road segment type every time +//they are needed. +typedef struct{ + float length; //the distance in meters from the beginning of the road (measured along the road) + float maxExtends; //used for clipping: the maximum distance from + //a road vertex from the point in the tRoadSeg's pos field. + tVector3 trackl,trackr; //the left-most and right-most points of the road, where AI players may drive + float speedIndex,reverseSpeedIndex; //tells the AI how fast to go on this road + float turboSpeedIndex,reverseTurboSpeedIndex; //tells the AI how fast to go on this road + float lateralAccel; + float maxGrip; + float track; //the racing line the ai will follow: 1 is all to the right of the road and -1 all to the left + tConvertedRoadVertex *vertices; //a pointer to the vertex coordinates for this road segment. +} tConvertedRoadSeg; + +typedef struct{ + tConvertedRoadVertex* vertexStorage; //the buffer storing all the vertex coordinates + tConvertedRoadSeg road[1]; //converted road segments +} tConvertedRoad; + +void CompileRoadPoints(int id); +void RoadRender(tVector3 *clipPlanes); +float RoadGetYValue(tVector3 point,int *lastRoadIndex,tVector3 *groundNormal,int *surfaceType,float *bumpOut); +void RoadCenterCar(tGameEntity *carEntity); +tVector3 RoadGetNextWaypoint(tVector3 pos,int *lastRoadIndex,float *overtaking,float *speedIndex,float aheadDistance); +float RoadGetPosition(tVector3 pos,int lastSegment,float *track); +tVector3 RoadGetDir(int segment); +void RoadInitCheckPoints(tVector3 startPos); +void RoadInitCheckPoints(tVector3 startPos,tVector3 endPos); +void RoadGetWayPointData(int startRoadSeg,float dist,float *minTrack,float *maxTrack,float *minSpeed); +float RoadGetLength(tVector3 pos,int lastSegment); +void RoadInitCornerSigns(); + +extern tRoadTypeList *gRoadTypes; +extern tSurfaceTypeList *gSurfaceTypes; +extern int gRoadRestrictedBorders,gQuickRoadCollision; +#endif diff --git a/source/roads~.cpp b/source/roads~.cpp new file mode 100755 index 0000000..9b357cb --- /dev/null +++ b/source/roads~.cpp @@ -0,0 +1,1069 @@ +//roads.cpp +//draws the road and detects collisions agains the road. + +#include +#include +#include +#include "entities.h" +#include "config.h" +#include "vectors.h" +#include "fileio.h" +#include "textures.h" +#include "gamemem.h" +#include "renderframe.h" +#include "roads.h" +#include "environment.h" +#include "gameinitexit.h" +#include "transparency.h" +#include "network.h" + + + +#include +#include "error.h" +#define kExtraLength 80 + +tRoadTypeList *gRoadTypes; +tSurfaceTypeList *gSurfaceTypes; +int gRoadRestrictedBorders=false; +int gQuickRoadCollision=false; + +#define kMaxAILateralAcceleration 4.0 +#define kMaxAITurboLateralAcceleration 12.0 +#define kMaxAILongitunalAcceleration 4.8 +#define kMaxAITurboLongitunalAcceleration 10.0 +#define kMaxSpeedIndex 100 + +tVector2 GetFlatTrack(tConvertedRoad *convRoad,int i,int roadSize) +{ + if(i<0)i=0; + if(i>roadSize-2)i=roadSize-2; + tVector3 pos_3=convRoad->road[i].trackl+(convRoad->road[i].trackr-convRoad->road[i].trackl)*((convRoad->road[i].track+1)*0.5); + return Vector(pos_3.x,pos_3.z); +} + +#define kMinSignLateralAccel 2.5 +#define kSignDistance 100.0 + +void CalcSpeedInfo(int ref) +{ + tRoadData *roadData=(tRoadData*)FileGetDataPtr(ref); + tRoadSeg *road=roadData->road; + tConvertedRoad *convRoad=(tConvertedRoad*)gFileTable[ref].parsedData; + + + for(int i=2;i<(roadData->roadSize)-2;i++) + { + float traction=gRoadTypes->types[road[i].type].traction; + if(traction==0)traction=1; + tVector2 pos1=GetFlatTrack(convRoad,i-7,(roadData->roadSize)); + tVector2 pos2=GetFlatTrack(convRoad,i,(roadData->roadSize)); + tVector2 pos3=GetFlatTrack(convRoad,i+7,(roadData->roadSize)); + tVector2 v1=!(pos2-pos1); + tVector2 v2=!(pos3-pos2); + float veloDiff=~(v2-v1); + float dist=~(pos1-pos2)+~(pos2-pos3); + + if(dist>0&&veloDiff>0) + { + float accel=dist/veloDiff; + convRoad->road[i].speedIndex=sqrt(kMaxAILateralAcceleration*accel*traction); + if(convRoad->road[i].speedIndex>kMaxSpeedIndex) + convRoad->road[i].speedIndex=kMaxSpeedIndex; + convRoad->road[i].turboSpeedIndex=sqrt(kMaxAITurboLateralAcceleration*accel*traction); + if(convRoad->road[i].turboSpeedIndex>kMaxSpeedIndex) + convRoad->road[i].turboSpeedIndex=kMaxSpeedIndex; + } + else + { + convRoad->road[i].speedIndex=kMaxSpeedIndex; + convRoad->road[i].turboSpeedIndex=kMaxSpeedIndex; + } + + convRoad->road[i].lateralAccel=(v1.y*v2.x-v2.y*v1.x)/dist*1000; + if(road[i].speedModifier>=-1&&road[i].speedModifier<=1) + { + convRoad->road[i].speedIndex*=(1+road[i].speedModifier); + convRoad->road[i].turboSpeedIndex*=(1+road[i].speedModifier); + } + convRoad->road[i].reverseSpeedIndex=convRoad->road[i].speedIndex; + convRoad->road[i].reverseTurboSpeedIndex=convRoad->road[i].turboSpeedIndex; + } + convRoad->road[0].speedIndex=kMaxSpeedIndex; + convRoad->road[1].speedIndex=kMaxSpeedIndex; + convRoad->road[(roadData->roadSize)-1].speedIndex=kMaxSpeedIndex; + convRoad->road[(roadData->roadSize)-2].speedIndex=kMaxSpeedIndex; + convRoad->road[0].reverseSpeedIndex=kMaxSpeedIndex; + convRoad->road[1].reverseSpeedIndex=kMaxSpeedIndex; + convRoad->road[(roadData->roadSize)-1].reverseSpeedIndex=kMaxSpeedIndex; + convRoad->road[(roadData->roadSize)-2].reverseSpeedIndex=kMaxSpeedIndex; + + convRoad->road[0].turboSpeedIndex=kMaxSpeedIndex; + convRoad->road[1].turboSpeedIndex=kMaxSpeedIndex; + convRoad->road[(roadData->roadSize)-1].turboSpeedIndex=kMaxSpeedIndex; + convRoad->road[(roadData->roadSize)-2].turboSpeedIndex=kMaxSpeedIndex; + convRoad->road[0].reverseTurboSpeedIndex=kMaxSpeedIndex; + convRoad->road[1].reverseTurboSpeedIndex=kMaxSpeedIndex; + convRoad->road[(roadData->roadSize)-1].reverseTurboSpeedIndex=kMaxSpeedIndex; + convRoad->road[(roadData->roadSize)-2].reverseTurboSpeedIndex=kMaxSpeedIndex; + + + + for(int i=(roadData->roadSize)-2;i>=0;i--) + { + float traction=gRoadTypes->types[road[i].type].traction; + if(traction==0)traction=1; + tVector2 pos1=Vector(road[i].pos.x,road[i].pos.z); + tVector2 pos2=Vector(road[i+1].pos.x,road[i+1].pos.z); + float dist=~(pos1-pos2); + float speedIndex2=convRoad->road[i+1].speedIndex; + float maxSpeedIndex1=sqrt(sqr(speedIndex2)+kMaxAILongitunalAcceleration*2*dist*traction); + float maxTurboSpeedIndex1=sqrt(sqr(speedIndex2)+kMaxAITurboLongitunalAcceleration*2*dist*traction); + if(convRoad->road[i].speedIndex>maxSpeedIndex1) + convRoad->road[i].speedIndex=maxSpeedIndex1; + if(convRoad->road[i].turboSpeedIndex>maxTurboSpeedIndex1) + convRoad->road[i].turboSpeedIndex=maxTurboSpeedIndex1; + } + for(int i=1;i<(roadData->roadSize)-2;i++) + { + float traction=gRoadTypes->types[road[i].type].traction; + if(traction==0)traction=1; + tVector2 pos1=Vector(road[i].pos.x,road[i].pos.z); + tVector2 pos2=Vector(road[i-1].pos.x,road[i-1].pos.z); + float dist=~(pos1-pos2); + float speedIndex2=convRoad->road[i-1].reverseSpeedIndex; + float maxSpeedIndex1=sqrt(sqr(speedIndex2)+kMaxAILongitunalAcceleration*2*dist*traction); + float maxTurboSpeedIndex1=sqrt(sqr(speedIndex2)+kMaxAITurboLongitunalAcceleration*2*dist*traction); + if(convRoad->road[i].reverseSpeedIndex>maxSpeedIndex1) + convRoad->road[i].reverseSpeedIndex=maxSpeedIndex1; + if(convRoad->road[i].reverseTurboSpeedIndex>maxTurboSpeedIndex1) + convRoad->road[i].reverseTurboSpeedIndex=maxTurboSpeedIndex1; + } + + int cornerStart=-1; + float corner=0; + float iminus2=convRoad->road[0].lateralAccel; + float iminus1=convRoad->road[1].lateralAccel; + //smooth lateralAccel + for(int i=2;i<(roadData->roadSize)-3;i++) + { + float temp=convRoad->road[i].lateralAccel; + convRoad->road[i].lateralAccel= + (convRoad->road[i].lateralAccel+ + convRoad->road[i+1].lateralAccel+ + convRoad->road[i+2].lateralAccel+ + iminus1+iminus2)/5; + iminus2=iminus1; + iminus1=temp; + } + + for(int i=1;i<(roadData->roadSize)-2;i++) + { + if((convRoad->road[i].lateralAccel>kMinSignLateralAccel&&corner>=0)|| + (convRoad->road[i].lateralAccel>1.5&&corner>0)) + { + if(corner==0) + cornerStart=i; + if(convRoad->road[i].lateralAccel>corner) + corner=convRoad->road[i].lateralAccel; + } + else if((convRoad->road[i].lateralAccel<-kMinSignLateralAccel&&corner<=0)|| + (convRoad->road[i].lateralAccel<-1.5&&corner<0)) + { + if(corner==0) + cornerStart=i; + if(convRoad->road[i].lateralAccelroad[i].lateralAccel; + } + else if(corner) + { + for(int j=cornerStart;jroad[j].lateralAccel=corner; + convRoad->road[i].lateralAccel=0; + corner=0; + cornerStart=-1; + } + else + convRoad->road[i].lateralAccel=0; + } + if(corner) + { + for(int j=cornerStart;j<(roadData->roadSize)-3;j++) + convRoad->road[j].lateralAccel=corner; + } +} + +float Interpolation(float x) +{ + return -x*x*x*x+2*x*x; +} + +float InterpolateTrack(float l1, float l2, float l, float track1, float track2) +{ + return track1+(track2-track1)*Interpolation((l-l1)/(l2-l1)); +} + +void ConvertRoadEndianess(tRoadData *roadData) +{ + tRoadSeg *road=roadData->road; + S32Swap(roadData->roadSize); + + for(int i=0;iroadSize;i++) + { + F32Swap(road[i].pos.x); + F32Swap(road[i].pos.y); + F32Swap(road[i].pos.z); + F32Swap(road[i].normal.x); + F32Swap(road[i].normal.y); + F32Swap(road[i].normal.z); + S32Swap(road[i].type); + F32Swap(road[i].track); + F32Swap(road[i].typeSeg); + } +} + + +void CompileRoadPoints(int id) +{ + tRoadData *roadData=(tRoadData*)FileGetDataPtr(id); + tRoadSeg *road=roadData->road; + + ConvertRoadEndianess(roadData); + + tConvertedRoad *convRoad=(tConvertedRoad*)MemoryAllocateBlock(((roadData->roadSize)-1)*sizeof(tConvertedRoadSeg)+sizeof(tConvertedRoad)); + + int vertexCount=0; + for(int i=0;i<(roadData->roadSize)-1;i++) + vertexCount+=gRoadTypes->types[(road[i].type)].numVertices+2; + + convRoad->vertexStorage=(tConvertedRoadVertex*)MemoryAllocateBlock(sizeof(tConvertedRoadVertex)*vertexCount); + + tVector3 prevSegDir,prevJunction2; + tVector3 curSegDir,curJunction1,curJunction2; + tVector3 nextSegDir,nextJunction1,nextJunction2; + tVector3 junction1,junction2; + + float totalLength=0; + float startTypeSeg=0; + vertexCount=0; + + curSegDir=road[1].pos-road[0].pos; + curJunction1=!(curSegDir%road[0].normal); + curJunction2=!(curSegDir%road[1].normal); + prevSegDir=curSegDir; + prevJunction2=curJunction1; + junction1=!(curJunction1+prevJunction2); + + if(road[0].track>1)road[0].track=0; + float lastTrackLength=0,lastTrack=road[0].track; + float nextTrackLength=0,nextTrack=road[0].track; + int lastTrackIndex=0,nextTrackIndex=0; + + for(int i=0;i<(roadData->roadSize)-1;i++) + { + if(nextTrackIndex<=i) + { + float l=totalLength; + for(int j=i+1;j<(roadData->roadSize)&&nextTrackIndex<=i;j++) + { + l+=~(road[j].pos-road[j-1].pos); + if(road[j].track<=1||j==(roadData->roadSize)-1) + { + lastTrackLength=nextTrackLength; + lastTrack=nextTrack; + lastTrackIndex=nextTrackIndex; + nextTrackIndex=j; + nextTrackLength=l; + nextTrack=(j==(roadData->roadSize)-1?road[0].track:road[nextTrackIndex].track); + } + } + } + + nextSegDir=i<(roadData->roadSize)-2?road[i+2].pos-road[i+1].pos:curSegDir; + nextJunction1=!(nextSegDir%road[i+1].normal); + nextJunction2=i<(roadData->roadSize)-2?!(nextSegDir%road[i+2].normal):nextJunction1; + + junction2=!(nextJunction1+curJunction1); + + convRoad->road[i].vertices=convRoad->vertexStorage+vertexCount+1; + convRoad->road[i].maxExtends=0; + + int numVertices=gRoadTypes->types[(road[i].type)].numVertices; + + vertexCount+=numVertices+2; + + convRoad->road[i].track=InterpolateTrack(lastTrackLength,nextTrackLength,totalLength,lastTrack,nextTrack); + convRoad->road[i].trackl=road[i].pos+junction1*gRoadTypes->types[(road[i].type)].minTrack; + convRoad->road[i].trackr=road[i].pos+junction1*gRoadTypes->types[(road[i].type)].maxTrack; + + for(int v=-1;vtypes[(road[i].type)].vertices[v+1].vertex1+(gRoadTypes->types[(road[i].type)].vertices[v+1].vertex2-gRoadTypes->types[(road[i].type)].vertices[v+1].vertex1)*startTypeSeg; + typePoint2=gRoadTypes->types[(road[i].type)].vertices[v+1].vertex1+(gRoadTypes->types[(road[i].type)].vertices[v+1].vertex2-gRoadTypes->types[(road[i].type)].vertices[v+1].vertex1)*road[i].typeSeg; + typePoint1.x-=3; + typePoint2.x-=3; + } + else if(v==numVertices) + { + typePoint1=gRoadTypes->types[(road[i].type)].vertices[v-1].vertex1+(gRoadTypes->types[(road[i].type)].vertices[v-1].vertex2-gRoadTypes->types[(road[i].type)].vertices[v-1].vertex1)*startTypeSeg; + typePoint2=gRoadTypes->types[(road[i].type)].vertices[v-1].vertex1+(gRoadTypes->types[(road[i].type)].vertices[v-1].vertex2-gRoadTypes->types[(road[i].type)].vertices[v-1].vertex1)*road[i].typeSeg; + typePoint1.x+=3; + typePoint2.x+=3; + } + else + { + typePoint1=gRoadTypes->types[(road[i].type)].vertices[v].vertex1+(gRoadTypes->types[(road[i].type)].vertices[v].vertex2-gRoadTypes->types[(road[i].type)].vertices[v].vertex1)*startTypeSeg; + typePoint2=gRoadTypes->types[(road[i].type)].vertices[v].vertex1+(gRoadTypes->types[(road[i].type)].vertices[v].vertex2-gRoadTypes->types[(road[i].type)].vertices[v].vertex1)*road[i].typeSeg; + } + + if(sqr(typePoint1)>convRoad->road[i].maxExtends) + convRoad->road[i].maxExtends=sqr(typePoint1); + if(sqr(typePoint2)>convRoad->road[i].maxExtends) + convRoad->road[i].maxExtends=sqr(typePoint2); + convRoad->road[i].vertices[v].v1=road[i].pos+road[i].normal*typePoint1.y+junction1*typePoint1.x; + convRoad->road[i].vertices[v].v2=road[i+1].pos+road[i+1].normal*typePoint2.y+junction2*typePoint2.x; + + convRoad->road[i].vertices[v].d2=convRoad->road[i].vertices[v].v2.y-convRoad->road[i].vertices[v].v1.y; + convRoad->road[i].vertices[v].d1=convRoad->road[i].vertices[v].v2.y-convRoad->road[i].vertices[v].v1.y; + if(i>0) + if((road[i-1].type)==(road[i].type)) + { + tVector3 v1=convRoad->road[i].vertices[v].v2-convRoad->road[i].vertices[v].v1; + tVector3 v2=convRoad->road[i].vertices[v].v1-convRoad->road[i-1].vertices[v].v1; + float l1=~v1; + float l2=~v2; + tVector3 v12=!(!v1+!v2); + float d=v12.y/sqrt(sqr(v12.x)+sqr(v12.z)); + if(fabs(convRoad->road[i].vertices[v].d1-d*l1)<2) + { + convRoad->road[i].vertices[v].d1=d*l1; + convRoad->road[i-1].vertices[v].d2=d*l2; + } + } + } + + startTypeSeg=road[i].typeSeg; + if(startTypeSeg>=1)startTypeSeg=0; + for(int v=-1;vroad[i].vertices[v].n1=!(junction1); + convRoad->road[i].vertices[v].n2=!(junction2); + } + else if(v==numVertices-1) + { + convRoad->road[i].vertices[v].n1=!(-junction1); + convRoad->road[i].vertices[v].n2=!(-junction2); + } + else + { + tVector2 typeNormal1=(gRoadTypes->types[(road[i].type)].vertices[v].vertex1-gRoadTypes->types[(road[i].type)].vertices[v+1].vertex1); + tVector2 typeNormal2=(gRoadTypes->types[(road[i].type)].vertices[v].vertex2-gRoadTypes->types[(road[i].type)].vertices[v+1].vertex2); + if(VectorEqual(typeNormal1,Vector(0,0))) + typeNormal1=Vector(-1,0); + if(VectorEqual(typeNormal2,Vector(0,0))) + typeNormal2=Vector(-1,0); + typeNormal1=!typeNormal1; + typeNormal2=!typeNormal2; + + convRoad->road[i].vertices[v].n1=!(-road[i].normal*typeNormal1.x+junction1*typeNormal1.y); + convRoad->road[i].vertices[v].n2=!(-road[i+1].normal*typeNormal2.x+junction2*typeNormal2.y); + } + } + + convRoad->road[i].length=totalLength; + totalLength+=~curSegDir; + + convRoad->road[i].maxExtends=sqrt(convRoad->road[i].maxExtends); + + prevJunction2=curJunction2; + curSegDir=nextSegDir; + curJunction1=nextJunction1; + curJunction2=nextJunction2; + junction1=junction2; + } + gFileTable[id].parsedData=convRoad; + gFileTable[id].parsed=true; + CalcSpeedInfo(id); +} + + +//Renders the Road +void RoadRender(tVector3 *clipPlanes) +{ + tRoadData *roadData=(tRoadData*)FileGetDataPtr(gMapInfo->road); + tRoadSeg *road=roadData->road; + if(!gFileTable[gMapInfo->road].parsed) + CompileRoadPoints(gMapInfo->road); + if(!gFileTable[gMapInfo->road].parsed) + return; + tConvertedRoad *convRoad=(tConvertedRoad*)gFileTable[gMapInfo->road].parsedData; + + SetupWorldTranslation(); + + glPushAttrib(GL_LIGHTING_BIT+GL_COLOR_BUFFER_BIT+GL_POLYGON_BIT); + +/* GLfloat specularReflectance[]={0.0,0.0,0.0,1}; + GLfloat ambientReflectance[]={0.6,0.6,0.6,1}; + GLfloat diffuseReflectance[]={0.4,0.4,0.4,1}; +*/ + GLfloat specularReflectance[]={0.0,0.0,0.0,1}; + GLfloat ambientReflectance[]={0.4,0.4,0.4,1}; + GLfloat diffuseReflectance[]={0.6,0.6,0.6,1}; + + glMaterialfv(GL_FRONT,GL_DIFFUSE,diffuseReflectance); + glMaterialfv(GL_FRONT,GL_AMBIENT,ambientReflectance); + glMaterialfv(GL_FRONT,GL_SPECULAR,specularReflectance); + glMaterialf(GL_FRONT,GL_SHININESS,20); + + int lastClipped=0; + int texture=-1; + + tPolyMaterial mat; + mat.flags=kMaterialBothSides+kMaterialUseAlphaChannel+kMaterialDisableWrapS; + mat.shininess=0; + mat.specular=Vector(0,0,0); + mat.ambient=Vector(0.6,0.6,0.6); + mat.diffuse=Vector(0.4,0.4,0.4); + + for(int i=0;i<(roadData->roadSize)-2;i++) + { + float len1=convRoad->road[i].length; + float len2=convRoad->road[i+1].length; + int clipped=ClipPointDistanced(clipPlanes,&road[i+1].pos,convRoad->road[i].maxExtends); + + if(!(clipped&lastClipped)) + { + int numVertices=gRoadTypes->types[(road[i].type)].numVertices; + for(int v=0;vtypes[(road[i].type)].vertices[v].texZoom; + if(texZoom>=0) + { + if(!(gRoadTypes->types[(road[i].type)].vertices[v].materialFlags&1)) + { + if(gRoadTypes->types[(road[i].type)].vertices[v].materialFlags&2) + glDisable(GL_CULL_FACE); + else + glEnable(GL_CULL_FACE); + + if(texture!=gRoadTypes->types[(road[i].type)].vertices[v].texture) + { + texture=gRoadTypes->types[(road[i].type)].vertices[v].texture; + TexturesSelectTex(texture); + if(!gConfig->carsOnSpeed) + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); + } + + glBegin(GL_TRIANGLE_STRIP); + + glNormal3fv(&convRoad->road[i].vertices[v].n2.x); + glTexCoord2f(gRoadTypes->types[(road[i].type)].vertices[v+1].texCoord1,texZoom?convRoad->road[i+1].length*texZoom:1); + glVertex3fv(&convRoad->road[i].vertices[v+1].v2.x); + + glNormal3fv(&convRoad->road[i].vertices[v].n1.x); + glTexCoord2f(gRoadTypes->types[(road[i].type)].vertices[v].texCoord2,texZoom?convRoad->road[i+1].length*texZoom:1); + glVertex3fv(&convRoad->road[i].vertices[v].v2.x); + + glNormal3fv(&convRoad->road[i].vertices[v].n2.x); + glTexCoord2f(gRoadTypes->types[(road[i].type)].vertices[v+1].texCoord1,texZoom?convRoad->road[i].length*texZoom:0); + glVertex3fv(&convRoad->road[i].vertices[v+1].v1.x); + + glNormal3fv(&convRoad->road[i].vertices[v].n1.x); + glTexCoord2f(gRoadTypes->types[(road[i].type)].vertices[v].texCoord2,texZoom?convRoad->road[i].length*texZoom:0); + glVertex3fv(&convRoad->road[i].vertices[v].v1.x); + + glEnd(); + /* + glBegin(GL_LINES); + glVertex3fv(&convRoad->road[i].vertices[v+1].v2.x); + glVertex3fv(&(convRoad->road[i].vertices[v+1].v2+convRoad->road[i].vertices[v].n2).x); + glVertex3fv(&convRoad->road[i].vertices[v+1].v1.x); + glVertex3fv(&(convRoad->road[i].vertices[v+1].v1+convRoad->road[i].vertices[v].n1*2).x); + glVertex3fv(&convRoad->road[i].vertices[v].v2.x); + glVertex3fv(&(convRoad->road[i].vertices[v].v2+convRoad->road[i].vertices[v].n2).x); + glVertex3fv(&convRoad->road[i].vertices[v].v1.x); + glVertex3fv(&(convRoad->road[i].vertices[v].v1+convRoad->road[i].vertices[v].n1*2).x); + glEnd();*/ + } + else if(tTransparentPoly *p=GetNextTransparentPoly(true)) + { + mat.texRef=gRoadTypes->types[(road[i].type)].vertices[v].texture; + + *p->mat=mat; + p->textureSet=0; + p->v[0].vertex=convRoad->road[i].vertices[v+1].v2; + p->v[1].vertex=convRoad->road[i].vertices[v].v2; + p->v[2].vertex=convRoad->road[i].vertices[v+1].v1; + p->v[0].normal=convRoad->road[i].vertices[v].n2; + p->v[1].normal=convRoad->road[i].vertices[v].n1; + p->v[2].normal=convRoad->road[i].vertices[v].n2; + p->v[0].texel=Vector(gRoadTypes->types[(road[i].type)].vertices[v+1].texCoord1,texZoom?convRoad->road[i+1].length*texZoom:1); + p->v[1].texel=Vector(gRoadTypes->types[(road[i].type)].vertices[v].texCoord2,texZoom?convRoad->road[i+1].length*texZoom:1); + p->v[2].texel=Vector(gRoadTypes->types[(road[i].type)].vertices[v+1].texCoord1,texZoom?convRoad->road[i].length*texZoom:0); + + if(tTransparentPoly *p=GetNextTransparentPoly(true)) + { + *p->mat=mat; + p->textureSet=0; + p->v[0].vertex=convRoad->road[i].vertices[v].v1; + p->v[1].vertex=convRoad->road[i].vertices[v].v2; + p->v[2].vertex=convRoad->road[i].vertices[v+1].v1; + p->v[0].normal=convRoad->road[i].vertices[v].n1; + p->v[1].normal=convRoad->road[i].vertices[v].n1; + p->v[2].normal=convRoad->road[i].vertices[v].n2; + p->v[0].texel=Vector(gRoadTypes->types[(road[i].type)].vertices[v].texCoord2,texZoom?convRoad->road[i].length*texZoom:0); + p->v[1].texel=Vector(gRoadTypes->types[(road[i].type)].vertices[v].texCoord2,texZoom?convRoad->road[i+1].length*texZoom:1); + p->v[2].texel=Vector(gRoadTypes->types[(road[i].type)].vertices[v+1].texCoord1,texZoom?convRoad->road[i].length*texZoom:0); + } + } + #ifdef __POLYCOUNT + gPolyCount+=2; + #endif + } + } + + } + lastClipped=clipped; + } + + glPopAttrib(); +} + + +tVector2 OrtoVec(tVector2 v) +{ + return Vector(v.y,-v.x); +} + +float GetPlaneHeight(tVector3 pos,tVector3 planeNormal,tVector3 planePoint) +{ + return -(planeNormal.x*pos.x+planeNormal.z*pos.z-planeNormal*planePoint)/planeNormal.y; +} + +int RoadGetRoadSegment(tVector3 pos,int lastSegment) +{ + tRoadData *roadData=(tRoadData*)FileGetDataPtr(gMapInfo->road); + tRoadSeg *road=roadData->road; + if(!gFileTable[gMapInfo->road].parsed) + CompileRoadPoints(gMapInfo->road); + if(!gFileTable[gMapInfo->road].parsed) + return 0; + tConvertedRoad *convRoad=(tConvertedRoad*)gFileTable[gMapInfo->road].parsedData; + + tVector2 flatPos=Vector(pos.x,pos.z); + + int max=(gRoadRestrictedBorders&&lastSegment)?15:(roadData->roadSize)*2; + + //a hack to always get a result for the camera, which jumps around a lot. + //if((lastSegment==gCameraEntity->lastRoadIndex)||gReplay||lastSegment==0) + // max=(roadData->roadSize)*2; + + for(int ind=1;indroadSize)-2; + while(i>=(roadData->roadSize)-2)i-=(roadData->roadSize)-2; + if(sqr(road[i].pos.x-flatPos.x)+sqr(road[i].pos.z-flatPos.y)road[i+1].length-convRoad->road[i].length+kExtraLength)) + { + int numVertices=gRoadTypes->types[(road[i].type)].numVertices; +// int endTopVertices=gRoadTypes->types[(road[i].type)].endTopVertices; +// int startTopVertices=gRoadTypes->types[(road[i].type)].startTopVertices; + int endTopVertices=gRoadTypes->types[(road[i].type)].numVertices+1; + int startTopVertices=-1; + tVector2 right1,left1,right2,left2; + tVector2 rDir=Vector(road[i+1].pos.x-road[i].pos.x,road[i+1].pos.z-road[i].pos.z); + do{ + right1=Vector(convRoad->road[i].vertices[startTopVertices].v1.x, convRoad->road[i].vertices[startTopVertices].v1.z ); + right2=Vector(convRoad->road[i].vertices[startTopVertices].v2.x, convRoad->road[i].vertices[startTopVertices].v2.z ); + startTopVertices++; + }while((right2-right1)*rDir<0); + do{ + left1= Vector(convRoad->road[i].vertices[endTopVertices-1].v1.x, convRoad->road[i].vertices[endTopVertices-1].v1.z ); + left2= Vector(convRoad->road[i].vertices[endTopVertices-1].v2.x, convRoad->road[i].vertices[endTopVertices-1].v2.z ); + endTopVertices--; + }while((left2-left1)*rDir<0); + if((right1-flatPos) *!OrtoVec(left1-right1)<=0.5) + if((left2-flatPos) *OrtoVec(right2-left2)<=0) + if((right1-flatPos) *!OrtoVec(right1-right2)<=0.5) + if((left2-flatPos) *OrtoVec(left2-left1)<=0) + return i; + } + } + return -1; +} + +float RoadGetPosition(tVector3 pos,int lastSegment) +{ + int i=RoadGetRoadSegment(pos,lastSegment); + if(i==-1)return 0; + + tRoadData *roadData=(tRoadData*)FileGetDataPtr(gMapInfo->road); + tRoadSeg *road=roadData->road; + + tVector3 point1=road[i].pos; + tVector3 point2=road[i+1].pos; + float localPosition=((pos-point1)*(point2-point1))/sqr(point2-point1); + return localPosition+i; +} + +float RoadGetLength(tVector3 pos,int lastSegment) +{ + float f=RoadGetPosition(pos,lastSegment); + int i=floor(f); + tRoadData *roadData=(tRoadData*)FileGetDataPtr(gMapInfo->road); + tConvertedRoad *convRoad=(tConvertedRoad*)gFileTable[gMapInfo->road].parsedData; + if(i<0)i=0; + if(iroadSize-1) + return convRoad->road[i].length+(convRoad->road[i+1].length-convRoad->road[i].length)*(f-i); + else + return convRoad->road[i].length; +} + +void RoadInitCheckPoints(tVector3 startPos) +{ + tRoadData *roadData=(tRoadData*)FileGetDataPtr(gMapInfo->road); + float pos=RoadGetPosition(startPos,0); + float stepSize=((roadData->roadSize)-3)/kNumCheckPoints; + for(int i=0;ireverse?-stepSize:stepSize; + if(pos>((roadData->roadSize)-3)) + pos-=((roadData->roadSize)-3); + if(pos<0) + pos+=(roadData->roadSize)-3; + + for(int j=0;jroad); + float pos1=RoadGetPosition(startPos,0); + float pos2=RoadGetPosition(endPos,0); +/* if(gGameInfo->reverse) + { + float tmp=pos2; + pos2=pos1; + pos1=tmp; + }*/ + float pos=pos1; + float stepSize=(pos2-pos1)/kNumCheckPoints; + + for(int i=0;iroad); + tConvertedRoad *convRoad=(tConvertedRoad*)gFileTable[gMapInfo->road].parsedData; + + float l=convRoad->road[initPos].length+length; + int i=initPos; + + if(l<0) + l+=convRoad->road[(roadData->roadSize)-2].length; + if(l>convRoad->road[(roadData->roadSize)-2].length) + l-=convRoad->road[(roadData->roadSize)-2].length; + + if(lroad[initPos].length) + while(i>0&&convRoad->road[i].length>l) + i--; + else + while(i<(roadData->roadSize)-1&&convRoad->road[i].lengthroad); + if(!gFileTable[gMapInfo->road].parsed) + CompileRoadPoints(gMapInfo->road); + if(!gFileTable[gMapInfo->road].parsed) + return; + tConvertedRoad *convRoad=(tConvertedRoad*)gFileTable[gMapInfo->road].parsedData; + gNumCornerSigns=0; + + float accel=0; + int lastCurvePos=0; + + if(gGameInfo->reverse) + for(int i=(roadData->roadSize)-3;i>0;i--) + { + if(convRoad->road[i].lateralAccel!=accel) + { + if(convRoad->road[i].lateralAccel!=0&&gNumCornerSignslastCurvePos) + gCornerSigns[gNumCornerSigns].pos=lastCurvePos; + gCornerSigns[gNumCornerSigns].accel=convRoad->road[i].lateralAccel; + gNumCornerSigns++; + } + accel=convRoad->road[i].lateralAccel; + lastCurvePos=i; + } + } + else for(int i=1;i<(roadData->roadSize)-2;i++) + { + if(convRoad->road[i].lateralAccel!=accel) + { + if(convRoad->road[i].lateralAccel!=0&&gNumCornerSignsroad[i].lateralAccel; + gNumCornerSigns++; + } + accel=convRoad->road[i].lateralAccel; + lastCurvePos=i; + } + } + +} + +tVector3 RoadGetDir(int segment) +{ + tRoadData *roadData=(tRoadData*)FileGetDataPtr(gMapInfo->road); + tRoadSeg *road=roadData->road; + + tVector3 point1=road[segment].pos; + tVector3 point2=road[segment+1].pos; + return !(gGameInfo->reverse?point1-point2:point2-point1); +} + +void RoadCenterCar(tGameEntity *carEntity) +{ + tRoadData *roadData=(tRoadData*)FileGetDataPtr(gMapInfo->road); + tRoadSeg *road=roadData->road; + tConvertedRoad *convRoad=(tConvertedRoad*)gFileTable[gMapInfo->road].parsedData; + + int i=RoadGetRoadSegment(carEntity->pos,carEntity->lastRoadIndex); + if(i==-1)return; + + + tVector3 point=(convRoad->road[i].trackl+convRoad->road[i].trackr)*0.5; + tVector3 dir=RoadGetDir(i); + *MatrixGetZVector(carEntity->dir)=dir; + *MatrixGetYVector(carEntity->dir)=Vector(0,1,0); + MatrixReAdjust(carEntity->dir); + carEntity->pos=point+((carEntity->pos-point)*dir)*dir; + carEntity->pos.y+=2+gRoadTypes->types[(road[i].type)].reviveY; + carEntity->pos=carEntity->pos-*MatrixGetXVector(carEntity->dir)*gRoadTypes->types[(road[i].type)].reviveX; + carEntity->velo=Vector(0,0,0); + MatrixIdentity(carEntity->rVelo); +} + +tVector3 RoadGetNextWaypoint(tVector3 pos,int *lastRoadIndex,float *overtaking,float *speedIndex,float aheadDistance) +{ + *lastRoadIndex=RoadGetRoadSegment(pos,*lastRoadIndex); + float l=RoadGetLength(pos,*lastRoadIndex)+(gGameInfo->reverse?-aheadDistance:aheadDistance); + int i=*lastRoadIndex; + tRoadData *roadData=(tRoadData*)FileGetDataPtr(gMapInfo->road); + tConvertedRoad *convRoad=(tConvertedRoad*)gFileTable[gMapInfo->road].parsedData; + + if(l<0) + l+=convRoad->road[(roadData->roadSize)-2].length; + if(l>convRoad->road[(roadData->roadSize)-2].length) + l-=convRoad->road[(roadData->roadSize)-2].length; + + if(l0&&convRoad->road[i].length>l) + i--; + else + while(i<(roadData->roadSize)-1&&convRoad->road[i].lengthreverse) + if(gGameInfo->arcade==2) + *speedIndex=convRoad->road[(int)f].reverseTurboSpeedIndex+(convRoad->road[((int)f)+1].reverseTurboSpeedIndex-convRoad->road[(int)f].reverseTurboSpeedIndex)*(f-(int)f); + else + *speedIndex=convRoad->road[(int)f].reverseSpeedIndex+(convRoad->road[((int)f)+1].reverseSpeedIndex-convRoad->road[(int)f].reverseSpeedIndex)*(f-(int)f); + else + if(gGameInfo->arcade==2) + *speedIndex=convRoad->road[(int)f].turboSpeedIndex+(convRoad->road[((int)f)+1].turboSpeedIndex-convRoad->road[(int)f].turboSpeedIndex)*(f-(int)f); + else + *speedIndex=convRoad->road[(int)f].speedIndex+(convRoad->road[((int)f)+1].speedIndex-convRoad->road[(int)f].speedIndex)*(f-(int)f); + + if(i<=0)i=1; + f=(l-convRoad->road[i-1].length)/(convRoad->road[i].length-convRoad->road[i-1].length); + if(f<0)f=0; + if(f>1)f=1; + + float track1=convRoad->road[i-1].track+(*overtaking>0?(1-convRoad->road[i-1].track)**overtaking:(1+convRoad->road[i-1].track)**overtaking); + tVector3 trackPos1=convRoad->road[i-1].trackl+((convRoad->road[i-1].trackr-convRoad->road[i-1].trackl)*((track1+1)/2)); + float track2=convRoad->road[i].track+(*overtaking>0?(1-convRoad->road[i].track)**overtaking:(1+convRoad->road[i].track)**overtaking); + tVector3 trackPos2=convRoad->road[i].trackl+((convRoad->road[i].trackr-convRoad->road[i].trackl)*((track2+1)/2)); + *overtaking=convRoad->road[i].track; + + return trackPos1+f*(trackPos2-trackPos1)+Vector(0,gRoadTypes->types[(roadData->road[i].type)].reviveY,0); +} + + +float InterpolateSplineAt(float y0,float y1,float d0,float d1,float x) +{ + float a= 2*y0-2*y1+ d0+ d1; + float b=-3*y0+3*y1-2*d0- d1; + float c= d0 ; + float d= y0 ; + return a*x*x*x+b*x*x+c*x+d; +} + +float RoadGetYValue(tVector3 point,int *lastRoadIndex,tVector3 *groundNormal,int *surfaceType,float *bumpOut) +{ + tRoadData *roadData=(tRoadData*)FileGetDataPtr(gMapInfo->road); + tRoadSeg *road=roadData->road; + if(!gFileTable[gMapInfo->road].parsed) + CompileRoadPoints(gMapInfo->road); + if(!gFileTable[gMapInfo->road].parsed) + return -INFINITY; + tConvertedRoad *convRoad=(tConvertedRoad*)gFileTable[gMapInfo->road].parsedData; + + tVector2 flatPos=Vector(point.x,point.z); + + int i=RoadGetRoadSegment(point,*lastRoadIndex); + if(i!=-1) + { + *lastRoadIndex=i; + int numVertices=gRoadTypes->types[(road[i].type)].numVertices; + int endTopVertices=gRoadTypes->types[(road[i].type)].endTopVertices; + int startTopVertices=gRoadTypes->types[(road[i].type)].startTopVertices; +// int endTopVertices=gRoadTypes->types[(road[i].type)].numVertices+1; +// int startTopVertices=-1; + + unsigned int min=startTopVertices,max=endTopVertices-1; + + tVector2 min1v2=Vector(convRoad->road[i].vertices[min].v1.x, convRoad->road[i].vertices[min].v1.z ); + tVector2 min2v2=Vector(convRoad->road[i].vertices[min].v2.x, convRoad->road[i].vertices[min].v2.z ); + if((min1v2-flatPos)*OrtoVec(min1v2-min2v2)>=0) + { + if(surfaceType) + *surfaceType=gMapInfo->baseSurfaceType; + if(groundNormal) + *groundNormal=convRoad->road[i].vertices[-1].n1; + if(bumpOut) + *bumpOut=0; + return -(min1v2-flatPos)*!OrtoVec(min1v2-min2v2); + } + tVector2 max1v2=Vector(convRoad->road[i].vertices[max].v1.x, convRoad->road[i].vertices[max].v1.z ); + tVector2 max2v2=Vector(convRoad->road[i].vertices[max].v2.x, convRoad->road[i].vertices[max].v2.z ); + if((max1v2-flatPos)*OrtoVec(max1v2-max2v2)<=0) + { + if(surfaceType) + *surfaceType=gMapInfo->baseSurfaceType; + if(groundNormal) + *groundNormal=convRoad->road[i].vertices[gRoadTypes->types[(road[i].type)].numVertices-1].n1; +// *groundNormal=!(road[i].pos-point); + if(bumpOut) + *bumpOut=0; + //printf("max %f\n",(max1v2-flatPos)*!OrtoVec(max1v2-max2v2)); + return (max1v2-flatPos)*!OrtoVec(max1v2-max2v2); + } + + while(max>min+1) + { + unsigned int mid=(min+max)/2; + tVector2 mid1=Vector(convRoad->road[i].vertices[mid].v1.x, convRoad->road[i].vertices[mid].v1.z ); + tVector2 mid2=Vector(convRoad->road[i].vertices[mid].v2.x, convRoad->road[i].vertices[mid].v2.z ); + if((mid1-flatPos)*OrtoVec(mid1-mid2)>=0) + max=mid; + else + min=mid; + } + int surface; + if(min!=-1&&max!=endTopVertices-1) + surface=gRoadTypes->types[(road[i].type)].vertices[min].surfaceType; + else + surface=gMapInfo->baseSurfaceType; + if(surfaceType) + *surfaceType=surface; + tVector3 min2=convRoad->road[i].vertices[min].v2; + tVector3 max2=convRoad->road[i].vertices[max].v2; + tVector3 min1=convRoad->road[i].vertices[min].v1; + tVector3 max1=convRoad->road[i].vertices[max].v1; + + tVector2 flatMin1=Vector(min1.x,min1.z); + tVector2 flatMin2=Vector(min2.x,min2.z); + tVector2 flatMax1=Vector(max1.x,max1.z); + tVector2 flatMax2=Vector(max2.x,max2.z); + + float u,v; + tVector2 a,b,c; + if(((flatMin1-flatPos)*OrtoVec(flatMin1-flatMax2)<=0||sqr(flatMax1-flatMin1)==0)&&!(sqr(flatMin2-flatMax2)==0)) + { + a=flatMin2-flatMax2; + b=flatMax1-flatMax2; + c=flatPos-flatMax2; + } + else + { + a=flatMax1-flatMin1; + b=flatMin2-flatMin1; + c=(flatMin1+a+b)-flatPos; + } + + if(a.x!=0&&a.y!=0&&b.x!=0&&b.y!=0){ + if(a.x==0) + { + v=(c.x)/(b.x); + u=(c.y-v*b.y)/(a.y); + } + else if(a.y==0) + { + v=(c.y)/(b.y); + u=(c.x-v*b.x)/(a.x); + } + else + { + u=(b.x*c.y-b.y*c.x)/(a.y*b.x-a.x*b.y); + if(b.y==0) + { + v=(c*a)/sqr(a); + } + else + v=(c.y-u*a.y)/b.y; + } + + + + // float miny=InterpolateSplineAt(min1.y,min2.y,convRoad->road[i].vertices[min].d1,convRoad->road[i].vertices[min].d2,1-v); + // float maxy=InterpolateSplineAt(max1.y,max2.y,convRoad->road[i].vertices[max].d1,convRoad->road[i].vertices[max].d2,1-v); + float miny=min2.y-v*(min2.y-min1.y); + float maxy=max2.y-v*(max2.y-max1.y); + float y=maxy-u*(maxy-miny); + + if(groundNormal) + { + tVector3 vmin=min2-v*(min2-min1); + vmin.y=miny; + tVector3 vmax=max2-v*(max2-max1); + vmax.y=maxy; + tVector3 v1=max1-u*(max1-min1); + tVector3 v2=max2-u*(max2-min2); + *groundNormal=!((vmin-vmax)%(v1-v2)); + } + + float bumpFreq=gSurfaceTypes->types[surface].bumpFreq; + float bump=gSurfaceTypes->types[surface].bumpHeight*sin(flatPos.x*bumpFreq)*sin(flatPos.y*bumpFreq); + if(bumpOut)*bumpOut=bump; + return point.y-y-bump; + } + else + { + tVector3 normal=!((min1-max2)%(max1-min2)); + + if(groundNormal) + *groundNormal=normal; + float bumpFreq=gSurfaceTypes->types[surface].bumpFreq; + float bump=gSurfaceTypes->types[surface].bumpHeight*sin(flatPos.x*bumpFreq)*sin(flatPos.y*bumpFreq); + if(bumpOut)*bumpOut=bump; + return point.y-GetPlaneHeight(point,normal,min2)-bump; + } + } + + if(!gQuickRoadCollision) + { + int closestRoadSeg=-1; + float closestRoadSegDist=INFINITY; + tVector2 normal; + for(int i=0;i<(roadData->roadSize)-2;i++) + { + if(sqr(road[i].pos.x-point.x)+sqr(road[i].pos.z-point.z)road[i+1].length-convRoad->road[i].length+kExtraLength)) + { + int numVertices=gRoadTypes->types[(road[i].type)].numVertices; + int endTopVertices=gRoadTypes->types[(road[i].type)].endTopVertices; + int startTopVertices=gRoadTypes->types[(road[i].type)].startTopVertices; + tVector2 right1=Vector(convRoad->road[i].vertices[startTopVertices].v1.x, convRoad->road[i].vertices[startTopVertices].v1.z ); + tVector2 left1= Vector(convRoad->road[i].vertices[endTopVertices-1].v1.x, convRoad->road[i].vertices[endTopVertices-1].v1.z ); + tVector2 right2=Vector(convRoad->road[i].vertices[startTopVertices].v2.x, convRoad->road[i].vertices[startTopVertices].v2.z ); + tVector2 left2= Vector(convRoad->road[i].vertices[endTopVertices-1].v2.x, convRoad->road[i].vertices[endTopVertices-1].v2.z ); + float dist; + tVector2 testNormal; + if(((right1-flatPos)*!OrtoVec(right1-left1))>=0) + if(((left2-flatPos)*!OrtoVec(right2-left2))<=0) + { + if((dist=(right1-flatPos)*(testNormal=!OrtoVec(right1-right2)))<=closestRoadSegDist) + if(dist>=0) + { + closestRoadSeg=i; + closestRoadSegDist=dist; + normal=testNormal; + } + if((dist=(left2-flatPos)*(testNormal=!OrtoVec(left2-left1)))<=closestRoadSegDist) + if(dist>=0) + { + closestRoadSeg=i; + closestRoadSegDist=dist; + normal=testNormal; + } + } + } + } + if(closestRoadSeg!=-1) + { + if(surfaceType) + *surfaceType=gMapInfo->baseSurfaceType; + if(groundNormal) + *groundNormal=Vector(normal.x,0,normal.y); + return -closestRoadSegDist; + } + } + if(surfaceType) + *surfaceType=gMapInfo->baseSurfaceType; + if(groundNormal) + *groundNormal=Vector(0,1,0); + return -INFINITY; +} + +void RoadGetWayPointData(int startRoadSeg,float dist,float *minTrack,float *maxTrack,float *minSpeed) +{ + tRoadData *roadData=(tRoadData*)FileGetDataPtr(gMapInfo->road); + tRoadSeg *road=roadData->road; + if(!gFileTable[gMapInfo->road].parsed) + CompileRoadPoints(gMapInfo->road); + if(!gFileTable[gMapInfo->road].parsed) + return; + tConvertedRoad *convRoad=(tConvertedRoad*)gFileTable[gMapInfo->road].parsedData; + float l=0; + *minTrack=1; + *maxTrack=-1; + *minSpeed=INFINITY; + while(lroad[startRoadSeg].track<*minTrack) + *minTrack=convRoad->road[startRoadSeg].track; + if(convRoad->road[startRoadSeg].track>*maxTrack) + *maxTrack=convRoad->road[startRoadSeg].track; + if(convRoad->road[startRoadSeg].speedIndex<*minSpeed) + *minSpeed=convRoad->road[startRoadSeg].speedIndex; + startRoadSeg++; + if(startRoadSeg>=(roadData->roadSize)-1) + startRoadSeg=0; + else + l+=convRoad->road[startRoadSeg].length-convRoad->road[startRoadSeg-1].length; + } +} diff --git a/source/screen.cpp b/source/screen.cpp new file mode 100755 index 0000000..b599173 --- /dev/null +++ b/source/screen.cpp @@ -0,0 +1,146 @@ +//screen.cpp +//initialize an OpenGL Context + +#include +#include +#include + +#include +#include "fileio.h" +#include "vectors.h" +#include "renderframe.h" +#include "gameframe.h" +#include "config.h" +#include "screen.h" +#include "gamemem.h" + +float gFOVY,gFOVX; //field-of-view in the x and y directions + +void InitGLContext(); +void ReInitGLContext(); +void ExitGLContext(); +void ScreenGetModes(); + +tVideoMode gVideoModes[kMaxModes]; +int gVideoNumModes=0; + +tScreenTexture gScreenTextures[4]; +int gScreenTexturesInited=false; +void SetupScreenTextures() +{ + for(int i=0;i<4;i++) + { + if(gScreenTexturesInited) + if(gScreenTextures[i].xSize) + glDeleteTextures(1,&gScreenTextures[i].texture); + gScreenTextures[i].xSize=gScreenTextures[i].ySize=0; + gScreenTextures[i].xOffset=gScreenTextures[i].yOffset=0; + glGenTextures(1,&gScreenTextures[i].texture); + glBindTexture(GL_TEXTURE_2D,gScreenTextures[i].texture); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); + } + int screenTextureXSize=1; + for(int i=0;i<16;i++) + if(screenTextureXSizescreenXSize) + screenTextureXSize*=2; + int screenTextureYSize=1; + for(int i=0;i<16;i++) + if(screenTextureYSizescreenYSize) + screenTextureYSize*=2; + + if(screenTextureXSize>1024) + if(screenTextureYSize>1024) + { + gScreenTextures[0].xSize=1024; + gScreenTextures[0].ySize=1024; + screenTextureXSize=1; + for(int i=0;i<16;i++) + if(screenTextureXSizescreenXSize-1024) + screenTextureXSize*=2; + gScreenTextures[1].xSize=screenTextureXSize; + gScreenTextures[1].ySize=1024; + gScreenTextures[1].xOffset=1024; + + screenTextureYSize=1; + for(int i=0;i<16;i++) + if(screenTextureYSizescreenYSize-1024) + screenTextureYSize*=2; + gScreenTextures[2].xSize=1024; + gScreenTextures[2].ySize=screenTextureYSize; + gScreenTextures[2].yOffset=1024; + gScreenTextures[3].xSize=screenTextureXSize; + gScreenTextures[3].ySize=screenTextureYSize; + gScreenTextures[3].xOffset=1024; + gScreenTextures[3].yOffset=1024; + } + else + { + gScreenTextures[0].xSize=1024; + gScreenTextures[0].ySize=screenTextureYSize; + + screenTextureXSize=1; + for(int i=0;i<16;i++) + if(screenTextureXSizescreenXSize-1024) + screenTextureXSize*=2; + gScreenTextures[1].xSize=screenTextureXSize; + gScreenTextures[1].ySize=screenTextureYSize; + gScreenTextures[1].xOffset=1024; + } + else + { + gScreenTextures[0].xSize=screenTextureXSize; + gScreenTextures[0].ySize=screenTextureYSize; + } + gScreenTexturesInited=true; +} + +void SetupAspect(float fovy) +{ + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); // Reset The Projection Matrix + + float aspect=(float)gConfig->screenXSize/(float)gConfig->screenYSize; + gFOVY=fovy; + gFOVX=aspect*gFOVY; + gluPerspective(gFOVY*(360.0/(2*PI)),aspect,0.1f,ClipDistance()); // Calculate The Aspect Ratio Of The Window + + glMatrixMode(GL_MODELVIEW); +} + +void InitGL() +{ + glEnable(GL_DEPTH_TEST); + glEnable(GL_LIGHTING); + glEnable(GL_CULL_FACE); + glEnable(GL_FOG); + glEnable(GL_TEXTURE_2D); + + float fogColor[4]={1,1,1,1}; + glFogi(GL_FOG_MODE,GL_LINEAR); + glFogf(GL_FOG_START,600); + glFogf(GL_FOG_END,800); + glFogfv(GL_FOG_COLOR,fogColor); + + if(gConfig->fsaa) + glEnable(GL_MULTISAMPLE_ARB); + + if(!ScreenSupportsAnisotropicFiltering()&&gConfig->textureFilter==2) + gConfig->textureFilter=1; + + SetupScreenTextures(); +} + +void ScreenInit() +{ + ScreenGetModes(); + InitGLContext(); + InitGL(); + SetupAspect(kNormalFOVY); +} + +void ScreenReInit() +{ + ScreenExit(); + ScreenInit(); +} diff --git a/source/screen.h b/source/screen.h new file mode 100755 index 0000000..8a19a3f --- /dev/null +++ b/source/screen.h @@ -0,0 +1,43 @@ +#ifndef __SCREEN +#define __SCREEN + +#include + +typedef struct{ + int xSize,ySize; + int xOffset,yOffset; + GLuint texture; +} tScreenTexture; + +typedef struct{ + int height,width,freq; +} tVideoMode; + +#define kMaxModes 256 +extern tVideoMode gVideoModes[]; +extern int gVideoNumModes; + +extern tScreenTexture gScreenTextures[4]; + +#define kNormalFOVY 0.7853981634 +//#define kNormalFOVY 0.9 +extern float gFOVY,gFOVX; + +#define kScreenXPos 0.52 +#define kScreenYPos 0.39 + +void ScreenInit(); +void ScreenReInit(); +void ScreenExit(); +void ScreenBlit(); +void SetupAspect(float fovy); +void InitGL(); +int GetVRAMSize(); +int ScreenSupportsTextureCompression(); +int ScreenSupports3DTextures(); +int ScreenSupportsAnisotropicFiltering(); +int ScreenSupportsBlendColor(); +int ScreenNoBigTextures(); +int ScreenNoWindow(); + +#endif \ No newline at end of file diff --git a/source/sky.cpp b/source/sky.cpp new file mode 100755 index 0000000..476598d --- /dev/null +++ b/source/sky.cpp @@ -0,0 +1,308 @@ +//sky.cpp +//draws the background skybox + +#include +#include +#include +#include "vectors.h" +#include "textures.h" +#include "fileio.h" +#include "renderframe.h" +#include "screen.h" +#include "environment.h" +#include "gameinitexit.h" +#include "entities.h" +#include "gameframe.h" +#include "random.h" +#include "config.h" + +#define kSkyDistance 7000.0 +#define kBackgroundDistance 5000.0 +#define kMapScale 1500.0 + +int gLightning=false; + +//draws the background skybox +void SkyRender() +{ + glPushAttrib(GL_FOG_BIT+GL_LIGHTING_BIT+GL_DEPTH_BUFFER_BIT+GL_COLOR_BUFFER_BIT+GL_TRANSFORM_BIT); + glDisable(GL_FOG); + glDisable(GL_LIGHTING); + glDepthFunc(GL_ALWAYS); + + SetupWorldTranslation(); + glMatrixMode(GL_PROJECTION); + + glPushMatrix(); + glLoadIdentity(); // Reset The Projection Matrix + + float aspect=(float)gConfig->screenXSize/(float)gConfig->screenYSize; + gluPerspective(gFOVY*(360.0/(2*PI)),aspect,0.1f,15000.0f); // Calculate The Aspect Ratio Of The Window + + + //==========draw skybox=============== + if(gMapEnv) + if(gMapEnv->sky0) + TexturesSelectTex(gMapEnv->sky0); + else + TexturesSelectTex(gEnvironment->sky0); + else + TexturesSelectTex(gEnvironment->sky0); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); + glBegin(GL_QUADS); + glTexCoord2f(0.0f,0.0f); + glVertex3f(-kSkyDistance,kSkyDistance,kSkyDistance); + glTexCoord2f(1.0f,0.0f); + glVertex3f(kSkyDistance,kSkyDistance,kSkyDistance); + glTexCoord2f(1.0f,1.0f); + glVertex3f(kSkyDistance,-kSkyDistance,kSkyDistance); + glTexCoord2f(0.0f,1.0f); + glVertex3f(-kSkyDistance,-kSkyDistance,kSkyDistance); + glEnd(); + + if(gMapEnv) + if(gMapEnv->sky270) + TexturesSelectTex(gMapEnv->sky270); + else + TexturesSelectTex(gEnvironment->sky270); + else + TexturesSelectTex(gEnvironment->sky270); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); + glBegin(GL_QUADS); + glTexCoord2f(0.0f,0.0f); + glVertex3f(-kSkyDistance,kSkyDistance,-kSkyDistance); + glTexCoord2f(1.0f,0.0f); + glVertex3f(-kSkyDistance,kSkyDistance,kSkyDistance); + glTexCoord2f(1.0f,1.0f); + glVertex3f(-kSkyDistance,-kSkyDistance,kSkyDistance); + glTexCoord2f(0.0f,1.0f); + glVertex3f(-kSkyDistance,-kSkyDistance,-kSkyDistance); + glEnd(); + + if(gMapEnv) + if(gMapEnv->sky180) + TexturesSelectTex(gMapEnv->sky180); + else + TexturesSelectTex(gEnvironment->sky180); + else + TexturesSelectTex(gEnvironment->sky180); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); + glBegin(GL_QUADS); + glTexCoord2f(0.0f,0.0f); + glVertex3f(kSkyDistance,kSkyDistance,-kSkyDistance); + glTexCoord2f(1.0f,0.0f); + glVertex3f(-kSkyDistance,kSkyDistance,-kSkyDistance); + glTexCoord2f(1.0f,1.0f); + glVertex3f(-kSkyDistance,-kSkyDistance,-kSkyDistance); + glTexCoord2f(0.0f,1.0f); + glVertex3f(kSkyDistance,-kSkyDistance,-kSkyDistance); + glEnd(); + + if(gMapEnv) + if(gMapEnv->sky90) + TexturesSelectTex(gMapEnv->sky90); + else + TexturesSelectTex(gEnvironment->sky90); + else + TexturesSelectTex(gEnvironment->sky90); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); + glBegin(GL_QUADS); + glTexCoord2f(0.0f,0.0f); + glVertex3f(kSkyDistance,kSkyDistance,kSkyDistance); + glTexCoord2f(1.0f,0.0f); + glVertex3f(kSkyDistance,kSkyDistance,-kSkyDistance); + glTexCoord2f(1.0f,1.0f); + glVertex3f(kSkyDistance,-kSkyDistance,-kSkyDistance); + glTexCoord2f(0.0f,1.0f); + glVertex3f(kSkyDistance,-kSkyDistance,kSkyDistance); + glEnd(); + + if(gMapEnv) + if(gMapEnv->skytop) + TexturesSelectTex(gMapEnv->skytop); + else + TexturesSelectTex(gEnvironment->skytop); + else + TexturesSelectTex(gEnvironment->skytop); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); + glBegin(GL_QUADS); + glTexCoord2f(0.0f,0.0f); + glVertex3f(-kSkyDistance,kSkyDistance,-kSkyDistance); + glTexCoord2f(1.0f,0.0f); + glVertex3f(kSkyDistance,kSkyDistance,-kSkyDistance); + glTexCoord2f(1.0f,1.0f); + glVertex3f(kSkyDistance,kSkyDistance,kSkyDistance); + glTexCoord2f(0.0f,1.0f); + glVertex3f(-kSkyDistance,kSkyDistance,kSkyDistance); + glEnd(); + + if(gMapEnv) + if(gMapEnv->skybot) + TexturesSelectTex(gMapEnv->skybot); + else + TexturesSelectTex(gEnvironment->skybot); + else + TexturesSelectTex(gEnvironment->skybot); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); + glBegin(GL_QUADS); + glTexCoord2f(0.0f,0.0f); + glVertex3f(-kSkyDistance,-kSkyDistance,kSkyDistance); + glTexCoord2f(1.0f,0.0f); + glVertex3f(kSkyDistance,-kSkyDistance,kSkyDistance); + glTexCoord2f(1.0f,1.0f); + glVertex3f(kSkyDistance,-kSkyDistance,-kSkyDistance); + glTexCoord2f(0.0f,1.0f); + glVertex3f(-kSkyDistance,-kSkyDistance,-kSkyDistance); + glEnd(); + + glPopMatrix(); + + + //=========draw lightning flashes======= + + //is there lighning in this environment? + if(gEnvironment->flashIntensity>0) + //and is there a flash at the moment? + if(gLightning) + { + glPushAttrib(GL_DEPTH_BUFFER_BIT+GL_FOG_BIT+GL_TEXTURE_BIT+GL_CURRENT_BIT+GL_TRANSFORM_BIT); + + gEnvironment->flashColor; + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glDisable(GL_TEXTURE_2D); + glDisable(GL_DEPTH_TEST); + + glEnable(GL_BLEND); +// glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + glBlendFunc(GL_DST_ALPHA,GL_ONE); + + //glEnable(GL_FOG); + //just draw a covering plane + glBegin(GL_TRIANGLE_STRIP); + glColor3fv(&(RandomFl(0,1)*gEnvironment->flashColor).x); + glVertex3f(-1.0f, 1.0f,-1.00f); + glColor3fv(&(RandomFl(0,1)*gEnvironment->flashColor).x); + glVertex3f(-1.0f,-1.0f,-1.00f); + glColor3fv(&(RandomFl(0,1)*gEnvironment->flashColor).x); + glVertex3f( 1.0f, 1.0f,-1.00f); + glColor3fv(&(RandomFl(0,1)*gEnvironment->flashColor).x); + glVertex3f( 1.0f,-1.0f,-1.00f); + glEnd(); + glPopMatrix(); + glPopAttrib(); + } + glPopAttrib(); + +/* glPushMatrix(); + glLoadIdentity(); // Reset The Projection Matrix + gluPerspective(gFOVY*(360.0/(2*PI)),aspect,0.1f,15000.0f); // Calculate The Aspect Ratio Of The Window + + glEnable(GL_LIGHTING); + glNormal3f(0,1,0); + + + //==========draw background=============== + //the background (mountains, for example) is as another box inside the skybox, + //which moves relative to the skybox as the camera moves, similar to paralax scrolling in 2d games. + +/* tVector3 backgroundShift; + backgroundShift.x=-(gCameraEntity->pos.x/kMapScale); + backgroundShift.y=-(gCameraEntity->pos.y/kMapScale); + backgroundShift.z=-(gCameraEntity->pos.z/kMapScale); + if(fabs(backgroundShift.x)>1)backgroundShift.x=sign(backgroundShift.x); + if(fabs(backgroundShift.y)>1)backgroundShift.y=sign(backgroundShift.y); + if(fabs(backgroundShift.z)>1)backgroundShift.z=sign(backgroundShift.z); + backgroundShift=backgroundShift*(kSkyDistance-kBackgroundDistance); + + TexturesSelectTex(gMapInfo->background0); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); + glBegin(GL_QUADS); + glTexCoord2f(0.0f,0.0f); + glVertex3f(-kBackgroundDistance+backgroundShift.x,kBackgroundDistance+backgroundShift.y,kBackgroundDistance+backgroundShift.z); + glTexCoord2f(1.0f,0.0f); + glVertex3f(kBackgroundDistance+backgroundShift.x,kBackgroundDistance+backgroundShift.y,kBackgroundDistance+backgroundShift.z); + glTexCoord2f(1.0f,1.0f); + glVertex3f(kBackgroundDistance+backgroundShift.x,-kBackgroundDistance+backgroundShift.y,kBackgroundDistance+backgroundShift.z); + glTexCoord2f(0.0f,1.0f); + glVertex3f(-kBackgroundDistance+backgroundShift.x,-kBackgroundDistance+backgroundShift.y,kBackgroundDistance+backgroundShift.z); + glEnd(); + + TexturesSelectTex(gMapInfo->background270); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); + glBegin(GL_QUADS); + glTexCoord2f(0.0f,0.0f); + glVertex3f(-kBackgroundDistance+backgroundShift.x,kBackgroundDistance+backgroundShift.y,-kBackgroundDistance+backgroundShift.z); + glTexCoord2f(1.0f,0.0f); + glVertex3f(-kBackgroundDistance+backgroundShift.x,kBackgroundDistance+backgroundShift.y,kBackgroundDistance+backgroundShift.z); + glTexCoord2f(1.0f,1.0f); + glVertex3f(-kBackgroundDistance+backgroundShift.x,-kBackgroundDistance+backgroundShift.y,kBackgroundDistance+backgroundShift.z); + glTexCoord2f(0.0f,1.0f); + glVertex3f(-kBackgroundDistance+backgroundShift.x,-kBackgroundDistance+backgroundShift.y,-kBackgroundDistance+backgroundShift.z); + glEnd(); + + TexturesSelectTex(gMapInfo->background180); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); + glBegin(GL_QUADS); + glTexCoord2f(0.0f,0.0f); + glVertex3f(kBackgroundDistance+backgroundShift.x,kBackgroundDistance+backgroundShift.y,-kBackgroundDistance+backgroundShift.z); + glTexCoord2f(1.0f,0.0f); + glVertex3f(-kBackgroundDistance+backgroundShift.x,kBackgroundDistance+backgroundShift.y,-kBackgroundDistance+backgroundShift.z); + glTexCoord2f(1.0f,1.0f); + glVertex3f(-kBackgroundDistance+backgroundShift.x,-kBackgroundDistance+backgroundShift.y,-kBackgroundDistance+backgroundShift.z); + glTexCoord2f(0.0f,1.0f); + glVertex3f(kBackgroundDistance+backgroundShift.x,-kBackgroundDistance+backgroundShift.y,-kBackgroundDistance+backgroundShift.z); + glEnd(); + + TexturesSelectTex(gMapInfo->background90); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); + glBegin(GL_QUADS); + glTexCoord2f(0.0f,0.0f); + glVertex3f(kBackgroundDistance+backgroundShift.x,kBackgroundDistance+backgroundShift.y,kBackgroundDistance+backgroundShift.z); + glTexCoord2f(1.0f,0.0f); + glVertex3f(kBackgroundDistance+backgroundShift.x,kBackgroundDistance+backgroundShift.y,-kBackgroundDistance+backgroundShift.z); + glTexCoord2f(1.0f,1.0f); + glVertex3f(kBackgroundDistance+backgroundShift.x,-kBackgroundDistance+backgroundShift.y,-kBackgroundDistance+backgroundShift.z); + glTexCoord2f(0.0f,1.0f); + glVertex3f(kBackgroundDistance+backgroundShift.x,-kBackgroundDistance+backgroundShift.y,kBackgroundDistance+backgroundShift.z); + glEnd(); + + glBegin(GL_QUADS); + glVertex3f(-kBackgroundDistance+backgroundShift.x,-kBackgroundDistance+backgroundShift.y,kBackgroundDistance+backgroundShift.z); + glVertex3f(kBackgroundDistance+backgroundShift.x,-kBackgroundDistance+backgroundShift.y,kBackgroundDistance+backgroundShift.z); + glVertex3f(kBackgroundDistance+backgroundShift.x,-kBackgroundDistance+backgroundShift.y,-kBackgroundDistance+backgroundShift.z); + glVertex3f(-kBackgroundDistance+backgroundShift.x,-kBackgroundDistance+backgroundShift.y,-kBackgroundDistance+backgroundShift.z); + glEnd(); + + glPopMatrix(); + + glPopAttrib();*/ + + + #ifdef __POLYCOUNT + gPolyCount+=20; + #endif +} \ No newline at end of file diff --git a/source/sky.h b/source/sky.h new file mode 100755 index 0000000..72c3258 --- /dev/null +++ b/source/sky.h @@ -0,0 +1 @@ +#ifndef __SKY #define __SKY extern int gLightning; void SkyRender(); #endif \ No newline at end of file diff --git a/source/sound_ST.cpp b/source/sound_ST.cpp new file mode 100644 index 0000000..9a1a506 --- /dev/null +++ b/source/sound_ST.cpp @@ -0,0 +1,466 @@ +#include +#include +#include "gamemem.h" +#include "vectors.h" +#include "entities.h" +#include "config.h" +#include "gamesound.h" +#include "gameinitexit.h" +#include "carphysics.h" +#include "roads.h" +#include "environment.h" +#include "random.h" +#include "gameframe.h" +#include "sky.h" + +typedef struct{ + STSoundRef engine,skid,turbo,noise,hall,wind,horn; + tFileRef skidSound; +// int enginePlaying,skidPlaying,turboPlaying,noisePlaying,hallPlaying,windPlaying,hornPlaying; +} tSoundSource; + +int gSoundLoopRunnig=false; +STSoundRef gSoundLoop; +int gThunder; + +void SoundPause() +{ + ST_SetAppVolume(0); +} + +void SoundInit() +{ + if(gConfig->soundEnable) + ST_Open(16,ST_DEFAULT_SAMPLE_RATE); + ST_SetAppVolume(gConfig->soundVolume*stMaxVolume); +} + +void SoundReInit() +{ + ST_SetAppVolume(gConfig->soundVolume*stMaxVolume); +} + + +#define kReferenceDistance 7 +#define kRollOfFactor 0.6 +#define kDopplerFactor 1 +#define kDopplerVelocity 331.45 + +//Callback to Loop Sounds +void SoundLoopCallback(long theMessage, STSoundCookie soundCookie,struct STSoundSpec *soundSpec) +{ + if(theMessage==stSoundFinished) + ST_PlaySoundParam(soundSpec); + else if(theMessage==stSoundBumped) + soundSpec->refCon=(void*)gFrameCount; +} + + +//calls ST_AdjustSound to setup 3d placement and custom volume/pitch of sound +void SetupSound(STSoundCookie s,tVector3 pos,tVector3 velo,float vol,float pitch,float prio) +{ + if(!s) + return; + tVector3 dir=pos-gCameraEntity->pos; + if(gConfig->carsOnSpeed) + dir=dir+Vector(30*sin(gFrameCount*0.01),10*sin(gFrameCount*0.1),20*sin(gFrameCount*0.008)); + float distance=~dir; + float panning=0; + if(distance) + { + dir=dir*1/distance; + float distanceFactor=kReferenceDistance/(kReferenceDistance+kRollOfFactor*(distance-kReferenceDistance)); + vol*=distanceFactor; + float listenerSpeed=-dir*gCameraEntity->velo; + if(gConfig->carsOnSpeed) + listenerSpeed+=90*sin(gFrameCount*0.001); + float sourceSpeed=dir*velo; + pitch=pitch+pitch*kDopplerFactor*((kDopplerVelocity-listenerSpeed)/(kDopplerVelocity+sourceSpeed)-1); + tMatrix3 m; + MatrixTranspose(gCameraEntity->dir,m); + dir=dir*m; + panning=dir.x; + } + + STSoundSpec spec; + ST_GetSoundSpec(s,&spec); + prio*=vol; + if(prio>1)prio=1; + spec.priority=prio*0xff; + spec.leftVol=0.1*ST_MAX_VOLUME*vol*(1+panning);//*gConfig->soundVolume; + spec.rightVol=0.1*ST_MAX_VOLUME*vol*(1-panning);//*gConfig->soundVolume; + spec.pitchAdjustment=ST_NORMAL_PITCH/pitch; + ST_AdjustSound(s,&spec); +} + +void LoadSound(tFileRef f) +{ + void *data=FileGetDataPtr(f); + void **soundData=IndirectInitialize(data,MemoryBlockSize(data)); + gFileTable[f].parsedData=MemoryAllocateBlock(sizeof(STSoundRef)); + ST_LoadIndirect(soundData,(STSoundRef*)gFileTable[f].parsedData); + IndirectDeallocate(soundData); + gFileTable[f].parsed=true; +} + +void LoadAllSounds() +{ + for(int i=0;isoundEnable&&!gBackgroundReplay) + { + if(!gFileTable[f].parsed) + LoadSound(f); + STSoundSpec spec; + + spec.sndCookie=NULL; + spec.theSound=*((STSoundRef*)gFileTable[f].parsedData); + spec.pitchAdjustment=ST_NORMAL_PITCH; + spec.STCallBack=loop?SoundLoopCallback:NULL; + if(silentStart) + { + spec.priority=0x00; + spec.leftVol=0;//*gConfig->soundVolume; + spec.rightVol=0;//*gConfig->soundVolume; + } + else + { + spec.priority=0x7f; + spec.leftVol=ST_MAX_VOLUME;//*gConfig->soundVolume; + spec.rightVol=ST_MAX_VOLUME;//*gConfig->soundVolume; + } + spec.soundSampleSize=16; + spec.refCon=(void*)-1L; + + return ST_PlaySoundParam(&spec); + } + else + return NULL; +} + +#define kMinSkidGainSlideVelo 0.9 +#define kMaxSkidGainSlideVelo 4.0 +#define kMinSkidPitchSlideVelo 1.2 +#define kMaxSkidPitchSlideVelo 15.0 +#define kMaxSoundDistance 120 +#define kEchoFadeTime 0.3 +#include "text.h" +void CarSoundEntity(int i) +{ + if(gBackgroundReplay) + return; + + tGameEntity *carEntity=gCarEntities[i]; + tCarPhysics *phys=(tCarPhysics*)carEntity->physics; + tCarDefinition *car=&(phys->car); + + //initialise Sound info, if it doesn't exist. + if(!carEntity->soundSource) + carEntity->soundSource=MemoryAllocateZeroedBlock(sizeof(tSoundSource)); + + //if car is far enough away from camera, silence all sound + if(sqr(carEntity->pos-gCameraEntity->pos)>sqr(kMaxSoundDistance)) + { + if(((tSoundSource*)carEntity->soundSource)->skid) + { + ST_HaltSound(((tSoundSource*)carEntity->soundSource)->skid); + ((tSoundSource*)carEntity->soundSource)->skid=NULL; + } + if(((tSoundSource*)carEntity->soundSource)->engine) + { + ST_HaltSound(((tSoundSource*)carEntity->soundSource)->engine); + ((tSoundSource*)carEntity->soundSource)->engine=NULL; + } + if(((tSoundSource*)carEntity->soundSource)->hall) + { + ST_HaltSound(((tSoundSource*)carEntity->soundSource)->hall); + ((tSoundSource*)carEntity->soundSource)->hall=NULL; + } + if(((tSoundSource*)carEntity->soundSource)->wind) + { + ST_HaltSound(((tSoundSource*)carEntity->soundSource)->wind); + ((tSoundSource*)carEntity->soundSource)->wind=NULL; + } + if(((tSoundSource*)carEntity->soundSource)->turbo) + { + ST_HaltSound(((tSoundSource*)carEntity->soundSource)->turbo); + ((tSoundSource*)carEntity->soundSource)->turbo=NULL; + } + if(((tSoundSource*)carEntity->soundSource)->horn) + { + ST_HaltSound(((tSoundSource*)carEntity->soundSource)->horn); + ((tSoundSource*)carEntity->soundSource)->horn=NULL; + } + return; + } + + //officially stop bumped sounds + STSoundSpec sp; + if(((tSoundSource*)carEntity->soundSource)->skid) + { + ST_GetSoundSpec(((tSoundSource*)carEntity->soundSource)->skid,&sp); + if(sp.refCon!=(void*)-1) + if(sp.refCon<(void*)(gFrameCount-5*(int)kFPS)) + ((tSoundSource*)carEntity->soundSource)->skid=NULL; + } + if(((tSoundSource*)carEntity->soundSource)->engine) + { + ST_GetSoundSpec(((tSoundSource*)carEntity->soundSource)->engine,&sp); + if(sp.refCon!=(void*)-1) + if(sp.refCon<(void*)(gFrameCount-5*(int)kFPS)) + ((tSoundSource*)carEntity->soundSource)->engine=NULL; + } + if(((tSoundSource*)carEntity->soundSource)->hall) + { + ST_GetSoundSpec(((tSoundSource*)carEntity->soundSource)->hall,&sp); + if(sp.refCon!=(void*)-1) + if(sp.refCon<(void*)(gFrameCount-5*(int)kFPS)) + ((tSoundSource*)carEntity->soundSource)->hall=NULL; + } + if(((tSoundSource*)carEntity->soundSource)->wind) + { + ST_GetSoundSpec(((tSoundSource*)carEntity->soundSource)->wind,&sp); + if(sp.refCon!=(void*)-1) + if(sp.refCon<(void*)(gFrameCount-5*(int)kFPS)) + ((tSoundSource*)carEntity->soundSource)->wind=NULL; + } + if(((tSoundSource*)carEntity->soundSource)->turbo) + { + ST_GetSoundSpec(((tSoundSource*)carEntity->soundSource)->turbo,&sp); + if(sp.refCon!=(void*)-1) + if(sp.refCon<(void*)(gFrameCount-5*(int)kFPS)) + ((tSoundSource*)carEntity->soundSource)->turbo=NULL; + } + if(((tSoundSource*)carEntity->soundSource)->horn) + { + ST_GetSoundSpec(((tSoundSource*)carEntity->soundSource)->horn,&sp); + if(sp.refCon!=(void*)-1) + if(sp.refCon<(void*)(gFrameCount-5*(int)kFPS)) + ((tSoundSource*)carEntity->soundSource)->horn=NULL; + } + + //engine sounds: + + //is the car in a tunnel? + int echo=gSurfaceTypes->types[phys->wheels[0].surfaceType].soundEcho; + + //fade echo status + if(phys->echoecho+=kFrameTime*(1/kEchoFadeTime); + if(phys->echo>echo) + phys->echo=echo; + } + if(phys->echo>echo) + { + phys->echo-=kFrameTime*(1/kEchoFadeTime); + if(phys->echoecho=echo; + } + + //if engine sound isn't running and echo<1, start engine sound + if(!((tSoundSource*)carEntity->soundSource)->engine&&phys->echo<1.0) + ((tSoundSource*)carEntity->soundSource)->engine=PlaySound(car->engineSample,true,true); + + //if halled engine sound isn't running and echo<1, start engine sound + if(!((tSoundSource*)carEntity->soundSource)->hall&&phys->echo>0.0) + ((tSoundSource*)carEntity->soundSource)->hall=PlaySound(car->hallSample,true,true); + + if(((tSoundSource*)carEntity->soundSource)->engine&&phys->echo>=1.0) + { + ST_HaltSound(((tSoundSource*)carEntity->soundSource)->engine); + ((tSoundSource*)carEntity->soundSource)->engine=NULL; + } + + if(((tSoundSource*)carEntity->soundSource)->hall&&phys->echo<=0.0) + { + ST_HaltSound(((tSoundSource*)carEntity->soundSource)->hall); + ((tSoundSource*)carEntity->soundSource)->hall=false; + } + + //Setup sound pitch/volume + float rpm=(phys->rpm-car->jerkRPM)/(car->maxRPM-car->jerkRPM); + float rpmGain=(car->zeroRPMSoundGain+rpm*(car->fullRPMSoundGain-car->zeroRPMSoundGain)); + float throttleGain=(car->zeroThrottleSoundGain+phys->throttle*(car->fullThrottleSoundGain-car->zeroThrottleSoundGain)); + + float rpmPitch=(car->zeroRPMSoundPitch+rpm*(car->fullRPMSoundPitch-car->zeroRPMSoundPitch)); + float throttlePitch=(car->zeroThrottleSoundPitch+phys->throttle*(car->fullThrottleSoundPitch-car->zeroThrottleSoundPitch)); + + if(((tSoundSource*)carEntity->soundSource)->engine) + SetupSound(((tSoundSource*)carEntity->soundSource)->engine,carEntity->pos,carEntity->velo,1.5*rpmGain*throttleGain*(1.0-phys->echo),rpmPitch*throttlePitch,1); + if(((tSoundSource*)carEntity->soundSource)->hall) + SetupSound(((tSoundSource*)carEntity->soundSource)->hall,carEntity->pos,carEntity->velo,1.5*rpmGain*throttleGain*(phys->echo),rpmPitch*throttlePitch,1); + + //skid + float slideVelo=0; + for(int i=0;inumWheels;i++) + slideVelo+=gSurfaceTypes->types[phys->wheels[i].surfaceType].soundEnable?phys->wheels[i].slipVelo:0; + slideVelo/=car->numWheels; + float skidGain=(slideVelo-kMinSkidGainSlideVelo)/(kMaxSkidGainSlideVelo-kMinSkidGainSlideVelo); + + if(!gSurfaceTypes->types[phys->wheels[0].surfaceType].soundEnable) + skidGain=0; + if(((tSoundSource*)carEntity->soundSource)->skid&&((tSoundSource*)carEntity->soundSource)->skidSound!=gSurfaceTypes->types[phys->wheels[0].surfaceType].skidSound) + skidGain=0; + if(skidGain<=0){ + if(((tSoundSource*)carEntity->soundSource)->skid) + { + ST_HaltSound(((tSoundSource*)carEntity->soundSource)->skid); + ((tSoundSource*)carEntity->soundSource)->skid=NULL; + } + } + else + if(!((tSoundSource*)carEntity->soundSource)->skid) + { + ((tSoundSource*)carEntity->soundSource)->skid=PlaySound(gSurfaceTypes->types[phys->wheels[0].surfaceType].skidSound,true,true); + ((tSoundSource*)carEntity->soundSource)->skidSound=gSurfaceTypes->types[phys->wheels[0].surfaceType].skidSound; + } + + if(((tSoundSource*)carEntity->soundSource)->skid) + { + if(skidGain<0)skidGain=0; + if(skidGain>1)skidGain=1; + + float skidPitch=(slideVelo-kMinSkidPitchSlideVelo)/(kMaxSkidPitchSlideVelo-kMinSkidPitchSlideVelo); + if(skidPitch<0)skidPitch=0; + if(skidPitch>1)skidPitch=1; + + SetupSound(((tSoundSource*)carEntity->soundSource)->skid,carEntity->pos,carEntity->velo,skidGain*2,(0.85+0.4*skidPitch),1); + } + + //wind sounds (player Car only) + if(carEntity!=gViewedEntity) + { + if(((tSoundSource*)carEntity->soundSource)->wind) + { + ST_HaltSound(((tSoundSource*)carEntity->soundSource)->wind); + ((tSoundSource*)carEntity->soundSource)->wind=NULL; + } + } + else + { + if(!((tSoundSource*)carEntity->soundSource)->wind) + ((tSoundSource*)carEntity->soundSource)->wind=PlaySound(FileGetReference("wind.wav"),true,true); + float velo=~carEntity->velo; + float windGain=velo/60.0; + float windPitch=velo/100; + if(windGain>1)windGain=1; + windGain*=1-CalcDraftFactor(carEntity); + if(windPitch>1)windPitch=1; + SetupSound(((tSoundSource*)carEntity->soundSource)->wind,carEntity->pos,carEntity->velo,windGain*2.5,1.0+0.6*windPitch,1); + } + + //turbo sounds + if(car->turboGain) + { + if(!((tSoundSource*)carEntity->soundSource)->turbo) + ((tSoundSource*)carEntity->soundSource)->turbo=PlaySound(car->turboSample,true,true); + + float turboAccel=(phys->rpm/car->maxRPM)*(0.3*phys->throttle+0.7)-0.1; + phys->turboRPM+=turboAccel*kFrameTime*3; + if(phys->turboRPM<0)phys->turboRPM=0; + float maxTurbo=(phys->rpm/car->maxRPM)*(0.5*phys->throttle+0.5); + if(phys->turboRPM>maxTurbo)phys->turboRPM-=(turboAccel>0?turboAccel*kFrameTime*3:0)+0.5*kFrameTime; + + float turboGain=phys->turboRPM; + float turboPitch=phys->turboRPM; + if(turboGain>1)turboGain=1; + if(turboPitch>1)turboPitch=1; + SetupSound(((tSoundSource*)carEntity->soundSource)->turbo,carEntity->pos,carEntity->velo,turboGain*car->turboGain*1.5,0.3+0.3*turboPitch,1); + } + + //horn + if(!(phys->lightFlags&kLightFlagHorn)){ + if(((tSoundSource*)carEntity->soundSource)->horn) + { + ST_HaltSound(((tSoundSource*)carEntity->soundSource)->horn); + ((tSoundSource*)carEntity->soundSource)->horn=NULL; + } + } + else + if(!((tSoundSource*)carEntity->soundSource)->horn) + { + tFileRef hornRef=car->hornSample; + if(!hornRef) + hornRef=FileGetReference("horn.wav"); + ((tSoundSource*)carEntity->soundSource)->horn=PlaySound(hornRef,true,true); + } + + if(((tSoundSource*)carEntity->soundSource)->horn) + SetupSound(((tSoundSource*)carEntity->soundSource)->horn,carEntity->pos,carEntity->velo,2,car->hornPitch+1,10); +} + + +void SoundFrame() +{ + if(gConfig->soundEnable) + { + //for each car process car's sound + for(int i=0;inumPlayers;i++) + CarSoundEntity(i); + + //environmental sounds (rain, thunder) + if(gEnvironment->soundLoopEnable) + { + if(!gSoundLoopRunnig) + { + gSoundLoopRunnig=true; + gSoundLoop=PlaySound(gEnvironment->soundLoop,true,false); + } + SetupSound(gSoundLoop,gCameraEntity->pos,gCameraEntity->velo,0.5+0.2*sin(gFrameCount*kFrameTime*0.5),1,10); + } + if(gEnvironment->soundRandomEnable) + { + if(gLightning) + gThunder=gFrameCount; + if(gFrameCount==gThunder+4*kFPS) + SetupSound(PlaySound(FileGetIndexedReference(gEnvironment->soundRandom,RandomInt(0,3)),false,false),gCameraEntity->pos,gCameraEntity->velo,RandomFl(0.5,1.5),1,1); + } + } +} + +//silence all sounds +void SoundSilence() +{ + if(gConfig->soundEnable&&!gBackgroundReplay) + ST_HaltSound(nil); + gSoundLoopRunnig=false; +} + +void SoundFreeEntitySources(tGameEntity *entity) +{ +} + +//plays a sound from a car's location +void CarPlayCrashNoise(tGameEntity *carEntity,tFileRef sound,float volume) +{ + if(gConfig->soundEnable&&!gBackgroundReplay&&carEntity->soundSource) + { + int isSoundPlaying=true; + if(!((tSoundSource*)carEntity->soundSource)->noise) + isSoundPlaying=false; + else if(!ST_IsSoundPlaying(((tSoundSource*)carEntity->soundSource)->noise)) + isSoundPlaying=false; + if(!isSoundPlaying) + { + ((tSoundSource*)carEntity->soundSource)->noise=PlaySound(sound,false,false); + SetupSound(((tSoundSource*)carEntity->soundSource)->noise,carEntity->pos,carEntity->velo,volume,1,2); + } + } +} + +//simple sound playback for user interface. +void PlayInterfaceSound(tFileRef sound) +{ + if(gConfig->interfaceSounds) + PlaySound(sound,false,false); +} + diff --git a/source/stencil.cpp b/source/stencil.cpp new file mode 100644 index 0000000..123dc61 --- /dev/null +++ b/source/stencil.cpp @@ -0,0 +1,63 @@ +//stencil.cpp +//draw a covering plane whereever stencil values are !=0, to simulate shadow and light volumes + +#include +#include "vectors.h" +#include "environment.h" +#include "screen.h" +#include "gamemem.h" +#include "config.h" + +float gStencilZoom=1.0; + +//draw a covering plane whereever stencil values are !=0 +//if shadow is true draw using shadow color to simulate shadow volumes +//else draw using spotlight color to simulate light cones. +void RenderStencilLayer(int shadow,int stencil) +{ + if(!stencil) + return; + glPushAttrib(GL_DEPTH_BUFFER_BIT+GL_LIGHTING_BIT+GL_COLOR_BUFFER_BIT+GL_TEXTURE_BIT+GL_CURRENT_BIT+GL_STENCIL_BUFFER_BIT); + + GLfloat shadowColor[4]; + if(shadow) + { + *(tVector3*)shadowColor=gEnvironment->shadowColor; + shadowColor[3]=gEnvironment->shadowIntensity; + shadowColor[3]/=stencil; + } + else + { + *(tVector3*)shadowColor=gEnvironment->spotLightColor*(1/(stencil*1.5)); + shadowColor[3]=1; + } + + + glColor4fv(shadowColor); + + glDisable(GL_LIGHTING); + glDisable(GL_TEXTURE_2D); + glDisable(GL_DEPTH_TEST); + + glEnable(GL_BLEND); + glEnable(GL_STENCIL_TEST); + + glStencilFunc(GL_NOTEQUAL, 1, 0xffffffff); + + glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); + + if(shadow) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + else + glBlendFunc(GL_DST_COLOR, GL_ONE); + + glLoadIdentity(); + glBegin(GL_TRIANGLE_STRIP); + glVertex3f(-2.0f, 1.0f,-1.00f); + glVertex3f(-2.0f,-1.0f,-1.00f); + glVertex3f( 2.0f, 1.0f,-1.00f); + glVertex3f( 2.0f,-1.0f,-1.00f); + glEnd(); + + glPopAttrib(); +} diff --git a/source/stencil.h b/source/stencil.h new file mode 100644 index 0000000..c79ab87 --- /dev/null +++ b/source/stencil.h @@ -0,0 +1,8 @@ +#ifndef __STENCIL +#define __STENCIL + +extern float gStencilZoom; + +void RenderStencilLayer(int shadow,int stencil); + +#endif \ No newline at end of file diff --git a/source/text.cpp b/source/text.cpp new file mode 100755 index 0000000..333813f --- /dev/null +++ b/source/text.cpp @@ -0,0 +1,532 @@ +//text.cpp +//texture-based OpenGL font drawing code + +#include +#include +#include +#include +#include "fileio.h" +#include "gamemem.h" +#include "text.h" +#include "config.h" +#include "parser.h" +#include "textures.h" +#include "renderframe.h" +#include "gameframe.h" +#include "environment.h" +#include "screen.h" +#include "gameinitexit.h" + + +#define kMessageBufferSize 128 + +#define kMessageSize 512 + +//one element of the Message Buffer, which will be drawn to the screen +//when TextPrintBuffer is called +typedef struct{ + char text[kMessageSize];//the actual string + float r,g,b,a; //color to draw text in + tVector3 pos; //position on screen + float size; //size of text + int startFadeIn; + int startFade,endFade; // + int align; //alignment of text + float tilt; +} tMessage; + +tMessage gMessageBuffer[kMessageBufferSize]; +int gMessagePos=0; +tFontInfo *gTextFont; +float gTextSize=0.03; +float gTextLineSpacing=0; +tVector2 gTextBufferScale=Vector(1,1); +float gTextOpacity=1; + +//loads the font described by the file referenced to by fontRef +void TextLoadFont(int fontRef) +{ + gTextFont=(tFontInfo*)FileGetParsedDataPtr(fontRef,kParserTypeFontInfoDesc,sizeof(tFontInfo)); + static int s=false; + if(!s){ + for(int i=0;inumChars;i++) + { + gTextFont->chars[i].x1+=0.003; + gTextFont->chars[i].x2-=0.003; + }s=true;} +} + + +void TextDrawSimple(char *text,float maxWidth,float size,int align) +{ + TexturesSelectTex(gTextFont->fontTexture); + //glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); + + glPushAttrib(GL_COLOR_BUFFER_BIT); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + + int l=0; + float xPos=0; + float yPos=0; + char *printPos=text; + float totalWidth=0; + while(*printPos!='\0') + { + if(*printPos=='\255') + { + do{ + printPos++; + l++; + }while(*printPos!='\255'&&*printPos!='\0'); + if(*printPos=='\0') + { + printPos--; + } + else + l++; + } + if(*printPos>=gTextFont->startChar&&*printPosstartChar+gTextFont->numChars) + { + char ch=*printPos; + if(ch>='a'&&ch<='z')ch+='A'-'a'; + + tFontCharInfo *cInfo=gTextFont->chars+(ch-gTextFont->startChar); + if(cInfo->y2-cInfo->y1>0) + if(totalWidth+(cInfo->x2-cInfo->x1)/(cInfo->y2-cInfo->y1)*sizex2-cInfo->x1)/(cInfo->y2-cInfo->y1)*size; + l++; + } + } + printPos++; + } + + printPos=text; + + if(align==kTextAlignRight) + xPos-=totalWidth; + else if(align==kTextAlignMiddle) + xPos-=totalWidth/2; + + int i=0; + while(printPos=gTextFont->startChar&&*printPosstartChar+gTextFont->numChars) + { + char ch=*printPos; + if(ch>='a'&&ch<='z')ch+='A'-'a'; + + tFontCharInfo *cInfo=gTextFont->chars+(ch-gTextFont->startChar); + if(cInfo->y2-cInfo->y1>0) + { + float width=(cInfo->x2-cInfo->x1)/(cInfo->y2-cInfo->y1)*size; + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(cInfo->x2,cInfo->y2); glVertex2f(xPos+width,yPos-size); + glTexCoord2d(cInfo->x2,cInfo->y1); glVertex2f(xPos+width,yPos); + glTexCoord2d(cInfo->x1,cInfo->y2); glVertex2f(xPos,yPos-size); + glTexCoord2d(cInfo->x1,cInfo->y1); glVertex2f(xPos,yPos); + glEnd(); + + #ifdef __POLYCOUNT + gPolyCount+=2; + #endif + xPos+=width; + } + } + printPos++; + } + + glPopAttrib(); +} + +float TextWidth(char *str,float size) +{ + float width=0; + while(*str!='\0') + { + if(*str>=gTextFont->startChar&&*strstartChar+gTextFont->numChars) + { + tFontCharInfo *cInfo=gTextFont->chars+(*str-gTextFont->startChar); + if(cInfo->y2-cInfo->y1>0) + width+=(cInfo->x2-cInfo->x1)/(cInfo->y2-cInfo->y1)*size; + } + if(*str=='\255') + { + str++; + char iconFile[256]; + int chi=0; + while(*str!='\255'&&*str!='\0') + iconFile[chi++]=*(str++); + iconFile[chi]='\0'; + if(iconFile[0]!='#') + width+=size; + } + str++; + } + return width; +} +//#include "error.h" +#define kTabSpaces 10 +//draws all the messages in the text buffer to the screen +void TextPrintBuffer(float opacity,int useInstrumentColor) +{ + gTexturesQualityModifier=-255; + int tmpFileError=gFileErrorReporting; + gFileErrorReporting=false; + glLoadIdentity(); + glTranslatef(0.0f,0.0f,0.0f); + glScalef(gTextBufferScale.x,gTextBufferScale.y,1); + + TexturesSelectTex(gTextFont->fontTexture); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR); + + glPushAttrib(GL_LIGHTING_BIT+GL_DEPTH_BUFFER_BIT+GL_COLOR_BUFFER_BIT+GL_CURRENT_BIT); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_LIGHTING); + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + + for(int i=gMessagePos-1;i>=0;i--) + if(gMessageBuffer[i].a>0) + { + float xPos=gMessageBuffer[i].pos.x; + float yPos=gMessageBuffer[i].pos.y; + float zPos=gMessageBuffer[i].pos.z; + if(fabs(zPos)>10) + glEnable(GL_DEPTH_TEST); + else + glDisable(GL_DEPTH_TEST); + if(zPos!=-1.0) + SetupAspect(1.0+~gCameraEntity->velo*0.0015*((gGameInfo->arcade!=3&&gGameInfo->arcade!=0)?(gGameInfo->arcade+1):0)); + else + SetupAspect(kNormalFOVY); + + float size=gMessageBuffer[i].size; + char *printPos=gMessageBuffer[i].text; + float totalWidth=TextWidth(printPos,size); + + printPos=gMessageBuffer[i].text; + if((gMessageBuffer[i].align&kTextAlignMask)==kTextAlignRight) + xPos-=totalWidth; + else if((gMessageBuffer[i].align&kTextAlignMask)==kTextAlignMiddle) + xPos-=totalWidth/2; + + float fade=1; + if(gMessageBuffer[i].startFade!=-1) + { + if(gFrameCount>gMessageBuffer[i].startFade) + fade=1-(gFrameCount-gMessageBuffer[i].startFade)/(float)(gMessageBuffer[i].endFade-gMessageBuffer[i].startFade); + else if(gFrameCountinstrumentColor.x*gMessageBuffer[i].r + ,gEnvironment->instrumentColor.y*gMessageBuffer[i].g + ,gEnvironment->instrumentColor.z*gMessageBuffer[i].b + ,fade*gMessageBuffer[i].a*opacity); + else + glColor4f(gMessageBuffer[i].r + ,gMessageBuffer[i].g + ,gMessageBuffer[i].b + ,fade*gMessageBuffer[i].a*opacity); + // PrintConsoleString(gMessageBuffer[i].text); + while(*printPos!='\0') + { + if(*printPos>=gTextFont->startChar&&*printPosstartChar+gTextFont->numChars) + { + tFontCharInfo *cInfo=gTextFont->chars+(*printPos-gTextFont->startChar); + if(cInfo->y2-cInfo->y1>0) + { + float width=(cInfo->x2-cInfo->x1)/(cInfo->y2-cInfo->y1)*size; + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(cInfo->x2,cInfo->y2); glVertex3f(xPos+width,yPos-size,zPos); + glTexCoord2d(cInfo->x2,cInfo->y1); glVertex3f(xPos+size*gMessageBuffer[i].tilt+width,yPos,zPos); + glTexCoord2d(cInfo->x1,cInfo->y2); glVertex3f(xPos,yPos-size,zPos); + glTexCoord2d(cInfo->x1,cInfo->y1); glVertex3f(xPos+size*gMessageBuffer[i].tilt,yPos,zPos); + glEnd(); + + #ifdef __POLYCOUNT + gPolyCount+=2; + #endif + xPos+=width; + } + } + else if(*printPos=='\t') + xPos=ceil(xPos*kTabSpaces/2)/(kTabSpaces/2); + else if(*printPos=='\255') + { + printPos++; + char iconFile[256]; + int chi=0; + while(*printPos!='\255'&&*printPos!='\0'&&chi<255) + iconFile[chi++]=*(printPos++); + //printPos++; + iconFile[chi]='\0'; + if(iconFile[0]=='#') + { + float r,g,b; + switch(iconFile[1]) + { + case 'w': r=1;g=1;b=1; break; + case 'r': r=1;g=0;b=0; break; + case 'g': r=0;g=1;b=0; break; + case 'b': r=0;g=0;b=1; break; + case 'y': r=1;g=1;b=0; break; + case 'c': r=0;g=1;b=1; break; + case 'm': r=1;g=0;b=1; break; + case 'a': r=0.8;g=0.8;b=0.8; break; + case 'd': r=0.6;g=0.6;b=0.6; break; + case 'l': r=0;g=0;b=0; break; + case 'n': r=gMessageBuffer[i].r;g=gMessageBuffer[i].g;b=gMessageBuffer[i].b; break; + case '0': r=0;g=0;b=0; break; + } + if(useInstrumentColor) + glColor4f(gEnvironment->instrumentColor.x*r + ,gEnvironment->instrumentColor.y*g + ,gEnvironment->instrumentColor.z*b + ,fade*gMessageBuffer[i].a*opacity); + else + glColor4f(r,g,b,fade*gMessageBuffer[i].a*opacity); + + } + else + { + tFileRef icon=FileGetReference(iconFile); + if(icon!=kFileErr) + { + char *extension=FileGetExtension(icon); + if(!(_stricmp(extension,".pct")&&_stricmp(extension,".tif")&&_stricmp(extension,".png")&&_stricmp(extension,".txr"))) + { + glPushAttrib(GL_CURRENT_BIT); + glColor4f(1,1,1,fade*gMessageBuffer[i].a*opacity); + TexturesSelectTex(icon); + + GLint width,height; + glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width); + glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height); + float ratio=height/(float)width; + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(1,1); glVertex3f(xPos+size,yPos-size*ratio,zPos); + glTexCoord2d(1,0); glVertex3f(xPos+size,yPos,zPos); + glTexCoord2d(0,1); glVertex3f(xPos,yPos-size*ratio,zPos); + glTexCoord2d(0,0); glVertex3f(xPos,yPos,zPos); + glEnd(); + glPopAttrib(); + TexturesSelectTex(gTextFont->fontTexture); + + xPos+=size; + } + } + } + } + if(gMessageBuffer[i].align==(kTextAlignLeft|kTextAutoWrap)) + if(xPos>0.43&&*printPos==' ') + { + xPos=gMessageBuffer[i].pos.x; + yPos-=size; + } + if(gMessageBuffer[i].align==(kTextAlignLeft|kTextAutoCut)) + if(xPos>0.49) + xPos=10; + if((*printPos=='\n'||*printPos=='\r')&&(gMessageBuffer[i].align&kTextAlignMask)==kTextAlignLeft) + { + xPos=gMessageBuffer[i].pos.x; + yPos-=size; + } + if(*printPos!='\0') + printPos++; + } + } + glPopAttrib(); + gFileErrorReporting=tmpFileError; + gTexturesQualityModifier=0; +} + +void TextClearBuffer() +{ + int fadeCount=0; + for(int i=0;i=kMessageBufferSize) + return; + va_list ap; // Pointer To List Of Arguments + + if (fmt == NULL) // If There's No Text + return; // Do Nothing + + va_start(ap, fmt); // Parses The String For Variables + gMessageBuffer[gMessagePos].pos=Vector(0,0,-1); + gMessageBuffer[gMessagePos].size=gTextSize; + gMessageBuffer[gMessagePos].startFade=-1; + gMessageBuffer[gMessagePos].endFade=-1; + gMessageBuffer[gMessagePos].r=1; + gMessageBuffer[gMessagePos].g=1; + gMessageBuffer[gMessagePos].b=1; + gMessageBuffer[gMessagePos].a=gTextOpacity; + gMessageBuffer[gMessagePos].align=kTextAlignLeft; + gMessageBuffer[gMessagePos].tilt=0; + vsprintf(gMessageBuffer[gMessagePos++].text,fmt,ap); // And Converts Symbols To Actual Numbers + va_end(ap); // Results Are Stored In Text +} + +void TextPrintfToBufferFormated(tVector2 pos,float size,int align,const char *fmt, ...) +{ + if(gMessagePos>=kMessageBufferSize) + return; + va_list ap; // Pointer To List Of Arguments + + if (fmt == NULL) // If There's No Text + return; // Do Nothing + + va_start(ap, fmt); // Parses The String For Variables + gMessageBuffer[gMessagePos].pos.x=pos.x*kTextXPos; + gMessageBuffer[gMessagePos].pos.y=pos.y*kTextYPos; + gMessageBuffer[gMessagePos].pos.z=-1; + gMessageBuffer[gMessagePos].size=size; + gMessageBuffer[gMessagePos].startFade=-1; + gMessageBuffer[gMessagePos].endFade=-1; + gMessageBuffer[gMessagePos].r=1; + gMessageBuffer[gMessagePos].g=1; + gMessageBuffer[gMessagePos].b=1; + gMessageBuffer[gMessagePos].a=gTextOpacity; + gMessageBuffer[gMessagePos].align=align; + gMessageBuffer[gMessagePos].tilt=0; + vsprintf(gMessageBuffer[gMessagePos++].text,fmt,ap); // And Converts Symbols To Actual Numbers + va_end(ap); // Results Are Stored In Text +} + +void TextPrintfToBufferFormatedVector3(tVector3 pos,float size,int align,const char *fmt, ...) +{ + if(gMessagePos>=kMessageBufferSize) + return; + va_list ap; // Pointer To List Of Arguments + + if (fmt == NULL) // If There's No Text + return; // Do Nothing + + va_start(ap, fmt); // Parses The String For Variables + pos.z=-pos.z; + pos.x=-pos.x; + gMessageBuffer[gMessagePos].pos=pos; + gMessageBuffer[gMessagePos].size=size; + gMessageBuffer[gMessagePos].startFade=-1; + gMessageBuffer[gMessagePos].endFade=-1; + gMessageBuffer[gMessagePos].r=1; + gMessageBuffer[gMessagePos].g=1; + gMessageBuffer[gMessagePos].b=1; + float dist=fabs(pos.z)/20.0; + if(dist>1)dist=1; + gMessageBuffer[gMessagePos].a=gTextOpacity*dist; + gMessageBuffer[gMessagePos].align=align; + gMessageBuffer[gMessagePos].tilt=0; + vsprintf(gMessageBuffer[gMessagePos++].text,fmt,ap); // And Converts Symbols To Actual Numbers + va_end(ap); // Results Are Stored In Text +} + +void TextPrintfToBufferFormatedColored(tVector2 pos,float size,int align,float r,float g,float b,float a,const char *fmt, ...) +{ + if(gMessagePos>=kMessageBufferSize) + return; + va_list ap; // Pointer To List Of Arguments + + if (fmt == NULL) // If There's No Text + return; // Do Nothing + + va_start(ap, fmt); // Parses The String For Variables + gMessageBuffer[gMessagePos].pos.x=pos.x*kTextXPos; + gMessageBuffer[gMessagePos].pos.y=pos.y*kTextYPos; + gMessageBuffer[gMessagePos].pos.z=-1; + gMessageBuffer[gMessagePos].size=size; + gMessageBuffer[gMessagePos].startFade=-1; + gMessageBuffer[gMessagePos].endFade=-1; + gMessageBuffer[gMessagePos].r=r; + gMessageBuffer[gMessagePos].g=g; + gMessageBuffer[gMessagePos].b=b; + gMessageBuffer[gMessagePos].a=a*gTextOpacity; + gMessageBuffer[gMessagePos].align=align; + gMessageBuffer[gMessagePos].tilt=0; + vsprintf(gMessageBuffer[gMessagePos++].text,fmt,ap); // And Converts Symbols To Actual Numbers + va_end(ap); // Results Are Stored In Text +} + +void TextPrintfToBufferFormatedColoredTilted(tVector2 pos,float size,int align,float tilt,float r,float g,float b,float a,const char *fmt, ...) +{ + if(gMessagePos>=kMessageBufferSize) + return; + va_list ap; // Pointer To List Of Arguments + + if (fmt == NULL) // If There's No Text + return; // Do Nothing + + va_start(ap, fmt); // Parses The String For Variables + gMessageBuffer[gMessagePos].pos.x=pos.x*kTextXPos; + gMessageBuffer[gMessagePos].pos.y=pos.y*kTextYPos; + gMessageBuffer[gMessagePos].pos.z=-1; + gMessageBuffer[gMessagePos].size=size; + gMessageBuffer[gMessagePos].startFade=-1; + gMessageBuffer[gMessagePos].endFade=-1; + gMessageBuffer[gMessagePos].r=r; + gMessageBuffer[gMessagePos].g=g; + gMessageBuffer[gMessagePos].b=b; + gMessageBuffer[gMessagePos].a=a*gTextOpacity; + gMessageBuffer[gMessagePos].align=align; + gMessageBuffer[gMessagePos].tilt=tilt; + vsprintf(gMessageBuffer[gMessagePos++].text,fmt,ap); // And Converts Symbols To Actual Numbers + va_end(ap); // Results Are Stored In Text +} + +void TextPrintfToBufferFormatedFading(tVector2 pos,float size,int align,float startFade,float endFade,const char *fmt, ...) +{ + if(gMessagePos>=kMessageBufferSize) + return; + + va_list ap; // Pointer To List Of Arguments + + if (fmt == NULL) // If There's No Text + return; // Do Nothing + + va_start(ap, fmt); // Parses The String For Variables + gMessageBuffer[gMessagePos].pos.x=pos.x*kTextXPos; + gMessageBuffer[gMessagePos].pos.y=pos.y*kTextYPos; + gMessageBuffer[gMessagePos].pos.z=-1; + gMessageBuffer[gMessagePos].size=size; + gMessageBuffer[gMessagePos].startFade=gFrameCount+startFade*kFPS; + gMessageBuffer[gMessagePos].endFade=gFrameCount+endFade*kFPS; + gMessageBuffer[gMessagePos].startFadeIn=gFrameCount; + gMessageBuffer[gMessagePos].r=1; + gMessageBuffer[gMessagePos].g=1; + gMessageBuffer[gMessagePos].b=1; + gMessageBuffer[gMessagePos].a=gTextOpacity*0.7; + gMessageBuffer[gMessagePos].align=align; + gMessageBuffer[gMessagePos].tilt=0; + vsprintf(gMessageBuffer[gMessagePos++].text,fmt,ap); // And Converts Symbols To Actual Numbers + va_end(ap); // Results Are Stored In Text +} + diff --git a/source/text.h b/source/text.h new file mode 100755 index 0000000..abe8586 --- /dev/null +++ b/source/text.h @@ -0,0 +1,50 @@ +#ifndef __TEXT +#define __TEXT + +#include "vectors.h" + +enum{ + kTextAlignLeft, + kTextAlignMiddle, + kTextAlignRight +}; + +#define kTextXPos kScreenXPos +#define kTextYPos kScreenYPos + +#define kFadingMessageXPos 0.0 +#define kFadingMessageYPos 0.6 +#define kFadingMessageSize 0.08 + +#define kTextAutoWrap (1<<31) +#define kTextAutoCut (1<<30) +#define kTextAlignMask (~(kTextAutoWrap|kTextAutoCut)) + +typedef struct{ + float x1,x2,y1,y2; +} tFontCharInfo; + +typedef struct{ + int fontTexture; + float ascent; + int startChar,numChars; + tFontCharInfo *chars; +} tFontInfo; + +extern tVector2 gTextBufferScale; +extern float gTextOpacity; + +float TextWidth(char *str,float size); +void TextDrawSimple(char *text,float maxWidth,float size,int align); +void TextLoadFont(int fontRef); +void TextPrintBuffer(float opacity,int useInstrumentColor); +void TextClearBuffer(); +void TextClearBufferFading(); +void TextPrintfToBuffer(const char *fmt, ...); +void TextPrintfToBufferFormated(tVector2 pos,float size,int align,const char *fmt, ...); +void TextPrintfToBufferFormatedVector3(tVector3 pos,float size,int align,const char *fmt, ...); +void TextPrintfToBufferFormatedFading(tVector2 pos,float size,int align,float startFade,float endFade,const char *fmt, ...); +void TextPrintfToBufferFormatedColored(tVector2 pos,float size,int align,float r,float g,float b,float a,const char *fmt, ...); +void TextPrintfToBufferFormatedColoredTilted(tVector2 pos,float size,int align,float tilt,float r,float g,float b,float a,const char *fmt, ...); + +#endif \ No newline at end of file diff --git a/source/textures.cpp b/source/textures.cpp new file mode 100755 index 0000000..c40c924 --- /dev/null +++ b/source/textures.cpp @@ -0,0 +1,364 @@ +//textures.cpp +//load textures into OpenGL + +#include +#include +#include +#include +#include +#include + +#include "fileio.h" +#include "gamemem.h" +#include "textures.h" +#include "texturesimport.h" +#include "config.h" +#include "gametime.h" +#include "error.h" +#include "screen.h" +#include "interfaceutil.h" +#include "writeout.h" +#include "S3Decompression.h" + +#define gCanMultiTexture true + +typedef struct{ + void *next; + tFileRef tex; +} tTextureList; + +typedef struct{ + GLuint ref; + GLenum target; +} tGLTextureInfo; + + + + +tTextureList *gTextureList=NULL; +int gEnableTextureLoad=true; +int gNumTexturesRequested=0; +int gNumTexturesLoaded=0; +int gTexturesQualityModifier=0; + +#define kTexturePriorityListFileName "texturepriorities.cfg" +//pass raw pixel data to OpenGL +void TexturesPassPixelsToGL(tFileRef tex,void *data,GLuint channels,GLuint pixType,GLuint pixSize,int xSize,int ySize,GLenum target,int quality) +{ + int xQual=(quality+1)/3; + int yQual=quality/3; + if(xQual<0)xQual=0; + if(yQual<0)yQual=0; + if((xSize>=1024||ySize>=1024)&&(xQual==0||yQual==0)) + if(ScreenNoBigTextures()) + xQual=yQual=1; + + //are we using reduced texture quality? + if(yQual) + { + //Calculate size of downsampled texture + int newXSize=xSize>>xQual>0?xSize>>xQual:1; + int newYSize=ySize>>yQual>0?ySize>>yQual:1; + + //allocate spce for downsampled texture + void *buffer=MemoryAllocateBlock(newXSize*newYSize*4); + + //create downsample texture + gluScaleImage(pixType,xSize,ySize,pixSize,data,newXSize,newYSize,pixSize,buffer); + gluBuild2DMipmaps(target,channels,newXSize,newYSize,pixType,pixSize,buffer); + + MemoryFreeBlock(buffer); + } + else + gluBuild2DMipmaps(target,channels,xSize,ySize,pixType,pixSize,data); +} + +void TexturesLoadImport(tFileRef tex,GLuint target,int quality) +{ + int xSize,ySize; + + //Get Buffer + void *imageBuffer=TexturesLoadImportBuffer(tex,&xSize,&ySize); + + //TexturesPassPixelsToGL(tex,imageBuffer,GL_COMPRESSED_RGBA_ARB,GL_RGBA,GL_UNSIGNED_BYTE,xSize,ySize,target,quality); + TexturesPassPixelsToGL(tex,imageBuffer,GL_RGBA,GL_RGBA,GL_UNSIGNED_BYTE,xSize,ySize,target,quality); + + MemoryFreeBlock(imageBuffer); +} + +void TexturesPassPixelsToGL3d(tFileRef tex,void *data,GLuint channels,GLuint pixType,GLuint pixSize,int xSize,int ySize,int zSize,GLenum target,int quality) +{ + quality/=3; + if(quality<0) + quality=0; + + //are we using reduced texture quality? + if(quality) + { + //Calculate size of downsampled texture + int newXSize=xSize>>quality>0?xSize>>quality:1; + int newYSize=ySize>>quality>0?ySize>>quality:1; + + //allocate spce for downsampled texture + void *buffer=MemoryAllocateBlock(newXSize*newYSize*zSize*4); + + //create downsample texture + for(int z=0;z> 8)); +} + +void LoadCompressedTexture(tFileRef tex,int quality) +{ + quality/=3; + if(quality<0)quality=0; + tTextureCacheFile *cache=(tTextureCacheFile*)FileGetDataPtr(tex); + tCachedTexture *t0=(tCachedTexture*)(((char*)cache)+EndianS32_BtoN(cache->offsets[0])); + if((t0->width>=1024||t0->height>=1024)&&quality==0) + if(ScreenNoBigTextures()) + quality=1; + for(int i=quality;inumEntries);i++) + { + tCachedTexture *t=(tCachedTexture*)(((char*)cache)+EndianS32_BtoN(cache->offsets[i])); + if(EndianS32_BtoN(t->width)&&EndianS32_BtoN(t->height)) + if(EndianS32_BtoN(t->compressionSize)) + if(ScreenSupportsTextureCompression()) + glCompressedTexImage2DARB( + EndianS32_BtoN(t->target), + EndianS32_BtoN(t->level)-quality, + EndianS32_BtoN(t->internalformat), + EndianS32_BtoN(t->width), + EndianS32_BtoN(t->height), + EndianS32_BtoN(t->border), + EndianS32_BtoN(t->compressionSize), + t->data + ); + else + { + UInt16* temp = (UInt16*)t->data; +#if TARGET_RT_BIG_ENDIAN + for (int i=0;icompressionSize/2;i++) + ByteSwap(temp++); +#endif + void* decompData=MemoryAllocateBlock(sizeof(GLuint)*EndianS32_BtoN(t->width)*EndianS32_BtoN(t->height)); + void* decompData2=MemoryAllocateBlock(sizeof(GLuint)*EndianS32_BtoN(t->width)*EndianS32_BtoN(t->height)); + DecompressDXT3(EndianS32_BtoN(t->width),EndianS32_BtoN(t->height),(UInt32*)t->data,(UInt32*)decompData); +#if TARGET_RT_BIG_ENDIAN + for(int y=0;yheight;y++) + for(int x=0;xwidth;x++) + { + UInt32 d=((UInt32*)decompData)[x+(y&1?y-1:y+1)*t->width]; + UInt8 b=d>>24; + UInt8 g=d>>16; + UInt8 r=d>>8; + UInt8 a=((UInt32*)decompData)[x+y*t->width]; + ((UInt32*)decompData2)[x+y*t->width]=(r<<24)|(g<<16)|(b<<8)|a; + } + glTexImage2D(EndianS32_BtoN(t->target) + ,EndianS32_BtoN(t->level)-quality + ,GL_RGBA + ,EndianS32_BtoN(t->width) + ,EndianS32_BtoN(t->height) + ,EndianS32_BtoN(t->border) + ,GL_RGBA,GL_UNSIGNED_BYTE,decompData2); +#else + glTexImage2D(EndianS32_BtoN(t->target) + ,EndianS32_BtoN(t->level)-quality + ,GL_RGBA + ,EndianS32_BtoN(t->width) + ,EndianS32_BtoN(t->height) + ,EndianS32_BtoN(t->border) + ,GL_RGBA,GL_UNSIGNED_BYTE,decompData); +#endif + + MemoryFreeBlock(decompData); + MemoryFreeBlock(decompData2); + } + else + glTexImage2D( + EndianS32_BtoN(t->target), + EndianS32_BtoN(t->level)-quality, + EndianS32_BtoN(t->internalformat), + EndianS32_BtoN(t->width), + EndianS32_BtoN(t->height), + EndianS32_BtoN(t->border), + EndianS32_BtoN(t->format), + EndianS32_BtoN(t->type), + t->data + ); + } +} + + +#include "error.h" +//load a texture from the file referenced by tex +void TexturesLoadFromFile(tFileRef tex,GLenum target) +{ + int quality=gConfig->textureQuality+gTexturesQualityModifier; + + //if the file type is a raw pixel format, just pass the data to OpenGL + char *fileExtension=FileGetExtension(tex); + if(!_stricmp(fileExtension,kFileTypeCompressedTexture)){ //precompressed texture + LoadCompressedTexture(tex,quality); + }else if(!_stricmp(fileExtension,kFileTypeRAW)){ //RAW RGB image + if(void *textureData=FileGetDataPtr(tex)){ + int size=sqrt(FileGetSize(tex)/3); + TexturesPassPixelsToGL(tex,textureData,GL_RGB,GL_RGB,GL_UNSIGNED_BYTE,size,size,target,quality); + } + }else if(!_stricmp(fileExtension,kFileTypeRAW3d)){ //RAW 3D RGBA image + if(void *textureData=FileGetDataPtr(tex)){ + int size=sqrt(FileGetSize(tex)/8); + if(ScreenSupports3DTextures()) + TexturesPassPixelsToGL3d(tex,textureData,GL_RGBA,GL_RGBA,GL_UNSIGNED_BYTE,size,size,2,target,quality); + else + TexturesPassPixelsToGL(tex,textureData,GL_RGBA,GL_RGBA,GL_UNSIGNED_BYTE,size,size,target,quality); + } + }else if(!_stricmp(fileExtension,kFileTypeGrayscaleRAW)){ //RAW grayscale image + if(void *textureData=FileGetDataPtr(tex)){ + int size=sqrt(FileGetSize(tex)); + TexturesPassPixelsToGL(tex,textureData,1,GL_LUMINANCE,GL_UNSIGNED_BYTE,size,size,target,quality); + } + }else if(!_stricmp(fileExtension,".RGBA")){ //RAW RGBA image + if(void *textureData=FileGetDataPtr(tex)){ + int size=sqrt(FileGetSize(tex)/4); + TexturesPassPixelsToGL(tex,textureData,GL_RGBA,GL_RGBA,GL_UNSIGNED_BYTE,size,size,target,quality); + } + } + //otherwise decompress image data + else TexturesLoadImport(tex,target,quality); //other image formats +} + +int TexturesSelectTextureUnit(int unit) +{ + GLint numTextureUnits; + glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB,&numTextureUnits); + if(unittarget=GL_TEXTURE_2D; + char *fileExtension=FileGetExtension(tex); + if(fileExtension) + if(!_stricmp(fileExtension,kFileTypeRAW3d)) + if(ScreenSupports3DTextures()) + info->target=GL_TEXTURE_3D; + + glGenTextures(1,&info->ref); + glBindTexture(info->target,info->ref); + //glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE,GL_TRUE); + //glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_STORAGE_HINT_APPLE , GL_STORAGE_SHARED_APPLE); + TexturesLoadFromFile(tex,info->target); + glTexParameteri(info->target,GL_TEXTURE_MAG_FILTER,GL_LINEAR); + glTexParameteri(info->target,GL_TEXTURE_MIN_FILTER,GL_LINEAR); + glTexParameteri(info->target,GL_TEXTURE_MIN_FILTER,gConfig->textureFilter?GL_LINEAR_MIPMAP_LINEAR:GL_LINEAR_MIPMAP_NEAREST); + if(gConfig->textureFilter==2) + glTexParameterf(info->target,GL_TEXTURE_MAX_ANISOTROPY_EXT,10); + + + tTextureList *textureList=gTextureList; + gTextureList=(tTextureList*)MemoryAllocateBlock(sizeof(tTextureList)); + gTextureList->next=(void*)textureList; + gTextureList->tex=tex; + gNumTexturesLoaded++; + + gFileTable[tex].parsedData=(void*)info; + gFileTable[tex].parsed=true; +} + +void TexturesUnloadAll() +{ + while(gTextureList!=NULL) + { + tTextureList *next=(tTextureList*)gTextureList->next; + glDeleteTextures(1,&((tGLTextureInfo*)(gFileTable[gTextureList->tex].parsedData))->ref); + MemoryFreeBlock(gFileTable[gTextureList->tex].parsedData); + gFileTable[gTextureList->tex].parsed=false; + FileReleaseData(gTextureList->tex); + MemoryFreeBlock(gTextureList); + gTextureList=next; + } +} + +//Selects the texture in the file referenced to by tex +void TexturesSelectTex(tFileRef tex) +{ + if(tex==kFileErr) + tex=FileGetReference("null.raw"); + + //is the texture loaded? + if(!gFileTable[tex].parsed) + TexturesLoadTex(tex); + if(gFileTable[tex].parsed){ + if(((tGLTextureInfo*)gFileTable[tex].parsedData)->target==GL_TEXTURE_3D) + { + glDisable(GL_TEXTURE_2D); + glEnable(GL_TEXTURE_3D); + } + else{ + glEnable(GL_TEXTURE_2D); + glDisable(GL_TEXTURE_3D); + } + // GLboolean b; + // if(!glAreTexturesResident(1,(GLuint*)gFileTable[tex].parsedData,&b)) + // SysBeep(0); + glBindTexture(((tGLTextureInfo*)gFileTable[tex].parsedData)->target,((tGLTextureInfo*)gFileTable[tex].parsedData)->ref); + } +} diff --git a/source/textures.h b/source/textures.h new file mode 100755 index 0000000..8ba3277 --- /dev/null +++ b/source/textures.h @@ -0,0 +1,42 @@ +#ifndef __TEXTURES +#define __TEXTURES + +#include "fileio.h" +#include + +typedef struct{ + tFileRef texture; + int priority; +} tTexturePriority; + +typedef struct{ + int numPriorities; + tTexturePriority *priorities; +} tTexturePriorityList; + +typedef struct{ + GLint compressionSize; + GLenum target; + GLint level; + GLint internalformat; + GLsizei width; + GLsizei height; + GLint border; + GLenum format; + GLenum type; + GLbyte data[1]; +} tCachedTexture; + +typedef struct{ + int numEntries; + int offsets[1]; +} tTextureCacheFile; + +extern int gEnableTextureLoad,gNumTexturesRequested,gNumTexturesLoaded,gTexturesQualityModifier; + +void TexturesSelectTex(tFileRef tex); +void TexturesAlphaFadeMipMaps(); +void TexturesUnloadAll(); +int TexturesSelectTextureUnit(int unit); + +#endif \ No newline at end of file diff --git a/source/texturesimport.h b/source/texturesimport.h new file mode 100644 index 0000000..5bb37dc --- /dev/null +++ b/source/texturesimport.h @@ -0,0 +1,13 @@ +#ifndef __TEXTURESIMPORT +#define __TEXTURESIMPORT + +#include + +typedef struct{ + char url[256]; + tFileRef offlineImage; +} tImageURL; + +void *TexturesLoadImportBuffer(tFileRef tex,int *xSize,int *ySize); + +#endif \ No newline at end of file diff --git a/source/tire.cpp b/source/tire.cpp new file mode 100644 index 0000000..0a5303c --- /dev/null +++ b/source/tire.cpp @@ -0,0 +1,249 @@ +#include + +#include "stdtypes.h" +#include "reg_tool_3.h" +#include "rt3_redline.h" + +#include "vectors.h" +#include "carphysics.h" +#include "gameframe.h" +#include "text.h" +#include "roads.h" + +#define CAMBER 0 + +/* +Race Car Vehicle Dynamices Ferrari Tire: +float a[15]={1.799,0,1688,4140,6.026,0,-0.3589,1,0,-6.111/1000,-3.244/100,0,0,0,0}; +float b[11]={1.65,0,1688,0,229,0,0,0,-10,0,0}; + +http://dynamic2.gamespy.com/~hg/forums/showthread.php?s=&threadid=49801 +??? +float a[15]={1.025,5,850,1200,46,0.5,-0.5,-0.5,0.5,-0.04,-0.08,0,0,0,0}; +float b[11]={1.5,5,1190,0.5,60,-0.05,-0.5,-0.05,0.25,0.05,-0.025}; + +205/45ZR16 +float a[15]={1,-106,1629,1843,9.4,0.013,-0.336,1.14,0.019,-0.019,-0.18,-20.7,-0.021,0.48,12.2}; +float b[11]={1.7,-80,1571,23.3,300,0,0.0069,0.055,-0.024,0.014,0.26}; + + +my testing values: +float a[15]={1.0,-120,1688,4140,6.026,0,-0.3589,1,0,-6.111/1000,-3.244/100,0,0,0,0}; +float b[11]={1.0,-120,1588,0,229,0,0,0,-10,0,0}; + +*/ + +float a[15]={1.0,-60,1688,4140,6.026,0,-0.3589,1,0,-6.111/1000,-3.244/100,0,0,0,0}; +float b[11]={1.0,-60,1588,0,229,0,0,0,-10,0,0}; + +float CalcLongitudinalForce(float Fz,float slip) +{ + Fz*=0.001;//convert to kN + slip*=100; //covert to % + float uP=b[1]*Fz+b[2]; + float D=uP*Fz; + float B=((b[3]*Fz+b[4])*exp(-b[5]*Fz))/(b[0]*uP); + float S=slip+b[9]*Fz+b[10]; + float E=b[6]*sqr(Fz)+b[7]*Fz+b[8]; + float Fx=D*sin(b[0]*atan(S*B+E*(atan(S*B)-S*B))); + return Fx; +} + +/* +float CalcLateralForce(float Fz,float slipAngle) +{ + Fz*=0.001;//convert to kN + slipAngle*=(360/(2*PI)); //convert angle to deg + float uP=a[1]*Fz+a[2]; + float D=uP*Fz; + float B=(a[3]*sin(2*atan(Fz/a[4]))*(1-a[5]*fabs(CAMBER)))/(a[0]*uP*Fz); + float S=slipAngle+a[8]*CAMBER+a[9]*Fz+a[10]; + float E=a[6]*Fz+a[7]; + float Sv=((a[11]*Fz+a[11])*CAMBER+a[12])*Fz+a[13]; + float Fy=D*sin(a[0]*atan(S*B+E*(atan(S*B)-S*B)))+Sv; + return Fy; +} +*/ +float CalcLateralForce(float Fz,float slipAngle) +{ + Fz*=0.001;//convert to kN + slipAngle*=(360/(2*PI)); //convert angle to deg + float uP=a[1]*Fz+a[2]; + float D=uP*Fz; + float B=(a[3]*sin(2*atan(Fz/a[4])))/(a[0]*uP*Fz); + float S=slipAngle+a[9]*Fz+a[10]; + float E=a[6]*Fz+a[7]; + float Sv=a[12]*Fz+a[13]; + float Fy=D*sin(a[0]*atan(S*B+E*(atan(S*B)-S*B)))+Sv; + return Fy; +} + +float CalcLongitudinalForceUnit(float Fz,float slip,tCarPhysics *phys) +{ + return CalcLongitudinalForce(Fz,slip*phys->maxSlip); +} + +float CalcLateralForceUnit(float Fz,float slipAngle,tCarPhysics *phys) +{ + return CalcLongitudinalForce(Fz,slipAngle*phys->maxAngle); +} + +void CalcCombinedForce(float Fz,tCarPhysics *phys,float *lateral,float *longitunal,float slip,float slipAngle) +{ + float unitSlip=slip/phys->maxSlip; + float unitAngle=slipAngle/phys->maxAngle; + float p=sqrt(sqr(unitSlip)+sqr(unitAngle)); + if(p){ + *longitunal=unitSlip/p*CalcLongitudinalForceUnit(Fz,p,phys); + *lateral=unitAngle/p*CalcLateralForceUnit(Fz,p,phys); + } + else + { + *longitunal=0; + *lateral=0; + } + /* + *longitunal=CalcLongitudinalForce(Fz,slip); + *lateral=CalcLateralForce(Fz,slipAngle); + if(*longitunal>Fz)*longitunal=Fz; + if(sqr(*lateral)+sqr(*longitunal)>sqr(Fz))*lateral=sqrt(sqr(Fz)-sqr(*longitunal))*sign(*lateral);*/ +} + +#define kStepSize 0.001 +#define kTestNormalForce 4000 + +void InitSlipMaxima(tCarDefinition *car,tCarPhysics *phys) +{ + float force=0; + for(float slip=kStepSize;;slip+=kStepSize) + { + float newForce=CalcLongitudinalForce(kTestNormalForce,slip); + if(forcemaxSlip=slip-kStepSize; + break; + } + } + force=0; + for(float slipAngle=kStepSize;;slipAngle+=kStepSize) + { + float newForce=CalcLateralForce(kTestNormalForce,slipAngle); + if(forcemaxAngle=slipAngle-kStepSize; + break; + } + } +} + +#define kFullSlipVelo 4.0 +#define kFullAngleVelo 2.0 + +float CalcSlipRatio(tVector3 wheelVelo,tVector3 wheelDir,float angularVelo,float radius) +{ + float wheelRoadVelo=wheelVelo*wheelDir; + if(wheelRoadVelo==0) + return 0; + float slipDamp=fabs(wheelRoadVelo)1) // to avoid precision errors causing nan's + sinSlipAngle=sign(sinSlipAngle)*1; + return VectorZero(wheelMotionDir)?0:asin(sinSlipAngle)*angleDamp*angleDamp; +} + + +#define kMaxSlipResAngularVelo 100.0 +#define kMaxSlipRes 10.0 + +tVector3 PacejkaCalcWheelRoadForces(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels,int i,float engineTorque,float frictionTorque) +{ + int isValid; + qRT3_LicenseTestOdd(phys->regCode,isValid); + + if(phys->maxSlip==0) + InitSlipMaxima(car,phys); + + tVector3 wheelVelo=wheels[i].velo; + tVector3 totalForce=Vector(0,0,0); + + if(phys->wheels[i].angularVelo<-50&&phys->gear>=0&&carEntity->controlType==kControlTypeAIInput) + { + phys->gear=0; + phys->rpm=0; + phys->wheels[i].angularVelo=10; + } + + int slipRes=(kMaxSlipResAngularVelo-fabs(phys->wheels[i].angularVelo))/(kMaxSlipResAngularVelo/kMaxSlipRes); + if(slipRes<1)slipRes=1; + + //adjust pajecka load sensitivity according to wheel width + a[1]=-200*1/(car->wheels[i].width*5+0.5)+50; + a[1]*=1+car->wheels[i].loadSensitivity; + b[1]=a[1]; + + float engineAngularDelta=engineTorque*kFrameTime/(slipRes*wheels[i].inertia); + float frictionAngularDelta=frictionTorque*kFrameTime/(slipRes*wheels[i].inertia); + float grip=(gSurfaceTypes->types+phys->wheels[i].surfaceType)->grip*(1+car->wheels[i].stickyness); + + tVector3 wheelDir,wheelNormal; + float slipRatio,slipAngle;float lateral=0; + static int damageDir=-1; + if(phys->wheels[i].angle!=wheels[i].oldAngle) + damageDir=sign(phys->wheels[i].angle-wheels[i].oldAngle); + for(int j=0;jwheels[i].angle-wheels[i].oldAngle)*((float)j/slipRes); + if(phys->damage>kSuspensionDamage&&i<2) + angle-=0.02*damageDir; + wheelDir=Vector(sin(angle),0,cos(angle)); + wheelDir=!((wheels[i].groundNormal%(wheelDir*carEntity->dir))%wheels[i].groundNormal); + wheelNormal=Vector(cos(angle),0,-sin(angle)); + wheelNormal=!((wheels[i].groundNormal%(wheelNormal*carEntity->dir))%wheels[i].groundNormal); + + slipRatio=CalcSlipRatio(wheelVelo,wheelDir,phys->wheels[i].angularVelo,car->wheels[i].radius); + slipAngle=CalcSlipAngle(wheelVelo,wheelDir,wheels[i].groundNormal); + + //wrong physics calculation if piracy detected + if((!isValid)&&(car->demoAvailable-car->numColors!=1)&&(carEntity==gCarEntities[gGameInfo->playerID])) + slipAngle=-slipRatio; + + float longitudinalForce,lateralForce; + CalcCombinedForce(wheels[i].normalForce,phys,&lateralForce,&longitudinalForce,slipRatio,slipAngle); + lateralForce*=grip; + longitudinalForce*=grip; + totalForce=totalForce+wheelDir*longitudinalForce-wheelNormal*lateralForce; + + lateral+=lateralForce/slipRes; + phys->wheels[i].angularVelo-=(longitudinalForce*car->wheels[i].radius)*kFrameTime/(slipRes*wheels[i].inertia); + phys->wheels[i].angularVelo+=engineAngularDelta; + if(fabs(frictionAngularDelta)wheels[i].angularVelo)) + phys->wheels[i].angularVelo-=frictionAngularDelta*sign(phys->wheels[i].angularVelo); + else + phys->wheels[i].angularVelo=0; + wheelVelo=wheelVelo+(wheelDir*longitudinalForce-wheelNormal*lateralForce)*((1/car->mass)*(kFrameTime/(float)slipRes)); + } + + float wheelRoadVelo=VectorZero(wheelVelo)?0:wheelVelo*wheelDir; + float longitunalSlipVelo=fabs(phys->wheels[i].angularVelo*car->wheels[i].radius-wheelRoadVelo); + float lateralSlipVelo=wheelVelo*wheelNormal; + phys->wheels[i].slipVelo=sqrt(sqr(longitunalSlipVelo)+sqr(lateralSlipVelo));//*wheels[i].normalForce*car->numWheels/(car->mass*9.81); + phys->wheels[i].slip=slipRatio; + phys->wheels[i].slipAngle=slipAngle; + + if(gGameInfo->arcade&&(!isValid)&&(car->demoAvailable-car->numColors!=1)&&(carEntity==gCarEntities[gGameInfo->playerID])) + phys->steering=slipRatio; + + return totalForce*((float)1/slipRes); +} diff --git a/source/tracker.cpp b/source/tracker.cpp new file mode 100644 index 0000000..e4734ce --- /dev/null +++ b/source/tracker.cpp @@ -0,0 +1,906 @@ +#include +#include +#include +#include +#include +#include +#include "tracker.h" +#include "gameinitexit.h" +#include "gametime.h" +#include "carphysics.h" +#include "config.h" +#include "text.h" +#include "gameframe.h" +#include "error.h" +#include "initexit.h" +#include "network.h" +#include "interfaceutil.h" +#include "reg_tool_3.h" +#include "gamesystem.h" +#include "screen.h" + +ReggieRef gReggieRef=NULL; +int gReggieConnected=false; +int gReggieSearching=false; +int gReggieLastUpdateGInfoVersion; +int gHasCheckedLonelyServer; +int gReggiePortMapped=false; +int gReggieCounting=false; +float gLastQuery=-1000; +float gConnectionTime; +float gReportedIdle=false; +char gMOTD[1024]; + +extern NT_SessionRef gSessionRef; +extern NetworkDatagramRef NT_GetSessionEndpoint(NT_SessionRef ref); + +#define kReggieUpdateInterval 1 + +int CouldMyNATCauseMyServerToBeLonely() { + NetworkAddress local; + DatagramGetAddress(NT_GetSessionEndpoint(gSessionRef),&local); + ReggieNetworkType network = ReggieProbeCheck(gReggieRef,false,&local,nil); + return((network != kReggieNetworkOffline) && (network <=kReggieNetworkProtective) ? true : false); +} + +#define kMessageInputBufferSize 8 +#define kMaxMessageSize 1024 +char gMessageInputBuffer[kMessageInputBufferSize][kMaxMessageSize]; +int gMessageInputBufferPos=0; +int gCheckingVersion=false; +char *gRedlineURL=NULL; +char *gAutoUpdateURL=NULL; + +void TrackerStartVersionCheck() +{ + if(NetworkStackGetActive(eNetworkStackTCPIP)) + { + ReggieVersionBegin(nil,"REDLINE"); + gCheckingVersion = true; + } +} + +int TrackerVersionCheck(char **newVersion) +{ + if(gCheckingVersion) + { + HashRef hashRef=ReggieVersionCheck(nil,"REDLINE"); + if(hashRef) + { + if(gConfig->useBetaBuilds) + { + gRedlineURL=HashLookup(hashRef, "$BETAURL", nil); + gAutoUpdateURL=HashLookup(hashRef, "$BETAAUTOUPDATEURL", nil); + Char8 *hashLookupResult; + int versionNum=0; + if(hashLookupResult=HashLookup(hashRef, "$BETAVERSIONNUM", nil)) + sscanf(hashLookupResult,"%d",&versionNum); + if(*newVersion=HashLookup(hashRef, "$BETAVERSION", nil)) + { + gCheckingVersion = false; /* Don't check again */ + if(versionNum) + return versionNum>kVersion; + else + return _stricmp(*newVersion,kVersionString); + } + } + else + { + gRedlineURL=HashLookup(hashRef, "$URL", nil); + gAutoUpdateURL=HashLookup(hashRef, "$AUTOUPDATEURL", nil); + Char8 *hashLookupResult; + int versionNum=0; + if(hashLookupResult=HashLookup(hashRef, "$VERSIONNUM", nil)) + sscanf(hashLookupResult,"%d",&versionNum); + if(*newVersion=HashLookup(hashRef, "$VERSION", nil)) + { + gCheckingVersion = false; /* Don't check again */ + if(versionNum) + return versionNum>kVersion; + else + return _stricmp(*newVersion,kVersionString); + } + } + } + } + return false; +} + +int TrackerConnect() +{ + gStartIdleTime=TimeGetSeconds(); + if(!gSessionRef) + NetworkPreSession(); + if(!ReggieCreateShared(&gReggieRef,NULL,NULL,NT_GetSessionEndpoint(gSessionRef),NULL,"redline",gConfig->playerName,"")) + { + gReggieLastUpdateGInfoVersion=-1; + gHasCheckedLonelyServer=false; + gConnectionTime=0; + gReggiePortMapped=false; + gReggieConnected=true; + gReggieSearching=false; + gReggieCounting=false; + return true; + } + return false; +} + +int ValidateUpdateURL() +{ + if(!gAutoUpdateURL) + return false; + char *pos=strstr(gAutoUpdateURL,"://"); + if(!pos)return false; + pos+=3; + char *dash=strstr(pos,"/"); + if(!dash)return false; + char validurl[]=".ambrosiasw.com"; + char test[256]; + memcpy(test,dash-strlen(validurl),strlen(validurl)); + test[strlen(validurl)]='\0'; + if(_stricmp(test,validurl)) {printf("update URL invaild\n");return false;} + return true; +} + +void TrackerGetNewVersion() +{ + if(ValidateUpdateURL()) + { + if(!ScreenNoWindow()) + { + if(gConfig->fullscreen) + { + gConfig->fullscreen=false; + ScreenReInit(); + gConfig->fullscreen=true; + } + InterfaceDrawStrings("Trying automatic update","Please wait a moment...",-1); + char updateURL[1024]; + char failStr[1024]=""; + sprintf(updateURL,"%s%s",gAutoUpdateURL,kVersionString); + if(AutoUpdateRedline(updateURL,failStr)) + // if(AutoUpdateRedline(gAutoUpdateURL)) + { + InterfaceDisplayMessage(-1,"Update successful!","You will have to relaunch Redline now."); + return; + } + else + InterfaceDisplayMessage(-1,"Automatic update failed.",failStr); + } + } + if(gRedlineURL) + { + CFStringRef str=CFStringCreateWithCString(kCFAllocatorDefault,gRedlineURL, kCFStringEncodingASCII); + CFURLRef url=CFURLCreateWithString(kCFAllocatorDefault,str,NULL); + //LSOpenCFURLRef(url,NULL); + LSLaunchURLSpec inLaunchSpec; + inLaunchSpec.appURL=NULL; + inLaunchSpec.itemURLs=CFArrayCreate(kCFAllocatorDefault,(const void**)&url,1,NULL); + inLaunchSpec.passThruParams=NULL; + inLaunchSpec.launchFlags=kLSLaunchNoParams; + inLaunchSpec.asyncRefCon=NULL; + LSOpenFromURLSpec(&inLaunchSpec,NULL); + } + else + { + CFURLRef url=CFURLCreateWithString(kCFAllocatorDefault,CFSTR("http://www.AmbrosiaSW.com/"),NULL); + //LSOpenCFURLRef(url,NULL); + LSLaunchURLSpec inLaunchSpec; + inLaunchSpec.appURL=NULL; + inLaunchSpec.itemURLs=CFArrayCreate(kCFAllocatorDefault,(const void**)&url,1,NULL); + inLaunchSpec.passThruParams=NULL; + inLaunchSpec.launchFlags=kLSLaunchNoParams; + inLaunchSpec.asyncRefCon=NULL; + LSOpenFromURLSpec(&inLaunchSpec,NULL); + } +} + +enum{ + kReggieAdvGameChannel=1, + kReggieSearchGameChannel, + kReggieSearchPlayersChannel=4, + kReggiePortMapChannel, + kReggieInsertRecordChannel, + kReggieRegistryChannel, + kReggieRecordChannel +}; + +void TrackerMessageSend(char *message) +{ + if(gReggieConnected) + { + HashRef hashRef = HashCreate(kHashFlagReggieKeys); + HashAppend(hashRef, "$KIND", "REDLINE"); + HashAppend(hashRef, "$MESSAGE", message); + + ReggieMessageSend(gReggieRef,nil,hashRef); + HashDispose(hashRef); + if(gMessageInputBufferPosstr,"### %s",motd); + msg->flags=kChatFlagSystem|kChatFlagSilent; + return true; + } + HashRef hash=ReggieMessageCheck(gReggieRef); + if(hash) + { + char *hashResult=nil; + do{ + hashResult=HashLookup(hash,"$MESSAGE",hashResult); + if(hashResult) + if(gMessageInputBufferPosstr,gMessageInputBuffer[--gMessageInputBufferPos]); + msg->flags=0; + if(strcmp(lastMessage,msg->str)) + { + strcpy(lastMessage,msg->str); + return true; + } + else + return false; + } + } + return false; +} + +void NetworkAdvertiseIdle(tGameInfo *gInfo,int *showLonelyMessage,int locked,int forceUpdate) +{ + if(showLonelyMessage) + *showLonelyMessage=false; + if(gReggieConnected) + { + UInt32 state=ReggieGetStatus(gReggieRef,gMOTD,NULL); + if(state==kReggieStateOnline) + { + if(gConnectionTime==0) + gConnectionTime=TimeGetSeconds(); + if((gReggieLastUpdateGInfoVersion!=gInfo->version||forceUpdate)&&gConfig->trackerEnable) + { + char hashString[1024]; + if(gInfo->map==kFileErr) + return; + tMapInfo *mapInfo=(tMapInfo*)FileGetParsedDataPtr(gInfo->map,kParserTypeMapInfoDesc,sizeof(tMapInfo)); + while(char *ch=strchr(gConfig->gameName,'\'')) + *ch='`'; + sprintf(hashString,"$REDLINE$HOSTING=1 $REDLINE$PASSWORD='%d' $REDLINE$NUMPLAYERS=%d $REDLINE$NUMNETPLAYERS=%d $REDLINE$MAXPLAYERS=%d $REDLINE$ARCADE=%d $REDLINE$LOCKED=%d $REDLINE$ONLYREGISTERED=%d $REDLINE$LAPS=%d",strlen(gConfig->password)?1:0,gInfo->numPlayers,gInfo->numNetPlayers,gConfig->maxPlayers,gInfo->arcade,locked,gConfig->onlyRegisteredPlayers,gInfo->numLaps); + HashRef hashRef = HashInitialize(kHashFlagReggieKeys,hashString); + HashAppend(hashRef, "$MESSAGE", gConfig->gameName); + HashAppend(hashRef, "$REDLINE$MAP", mapInfo->name); + HashAppend(hashRef, "$REDLINE$MAPFILE",FileGetName(gInfo->map)); + + for(int i=0;inumPlayers;i++) + { + NT_MemberInfo memberInfo; + char key[256]; + sprintf(key,"$REDLINE$PLAYER%dNAME",i); + HashAppend(hashRef, key, gInfo->playerNames[i]); + if(gInfo->playerCars[i]!=-1) + { + tCarDefinition *car=(tCarDefinition*)FileGetParsedDataPtr(gInfo->playerCars[i],kParserTypeCarDesc,sizeof(tCarDefinition)); + sprintf(key,"$REDLINE$PLAYER%dCAR",i); + HashAppend(hashRef, key, car->carName); + } + if(i>0&&inumNetPlayers) + { + if(NT_GetMemberInfo(gSessionRef,gInfo->netID[i],&memberInfo)) + { + char address[256]; + AddressTranscribe(&memberInfo.address,address); + sprintf(key,"$ADDRESS$PLAYER%d",i); + HashAppend(hashRef, key, address); + } + } + else if(i==0) + { + NetworkAddress local,publicAdd; + char address[256]; + DatagramGetAddress(NT_GetSessionEndpoint(gSessionRef),&local); + ReggieProbeCheck(gReggieRef,false,&local,&publicAdd); + if(!AddressIsEmpty(&publicAdd)) + { + AddressTranscribe(&publicAdd,address); + HashAppend(hashRef, "$ADDRESS$PLAYER0", address); + } + } + } + + ReggieInsertBegin(gReggieRef,kReggieAdvGameChannel, kReggieRecordAdvertise ,hashRef);/* Makes own copy of hash */ + HashDispose(hashRef); /* Not needed any more */ + gReggieLastUpdateGInfoVersion=gInfo->version; + } + if(!gHasCheckedLonelyServer) + if(TimeGetSeconds()>gConnectionTime+3) + if(showLonelyMessage) + { + *showLonelyMessage=CouldMyNATCauseMyServerToBeLonely(); + //printf("CouldMyNATCauseMyServerToBeLonely sez %d\n",*showLonelyMessage); + gHasCheckedLonelyServer=true; + } + if(!gReggiePortMapped) + if(TimeGetSeconds()>gConnectionTime+1) + { + NetworkAddress local; + UInt16 localPort; + DatagramGetAddress(NT_GetSessionEndpoint(gSessionRef),&local); + AddressDecomposeTCPIP(&local,&localPort,nil); + U16Swap(localPort); + //printf("PORTMAP: %d\n",ReggiePortmapStatus(gReggieRef, nil, nil, &local)); + //if (ReggiePortmapStatus(gReggieRef, nil, nil, &local) > kReggiePortmapNone) + ReggiePortmapBegin(gReggieRef,kReggiePortMapChannel,false,localPort,localPort,"Redline"); + gReggiePortMapped=true; + } + } + else if(state!=kReggieStatePending) + { + NetworkStopAdvertising(); + ReggieDispose(gReggieRef,true); + gReggieConnected=false; + } + } + else + TrackerConnect(); +} + +void NetworkStopAdvertising() +{ + if(gReggieConnected) + { + ReggieChannelClose(gReggieRef,kReggieAdvGameChannel); +// ReggieDispose(gReggieRef,true); +// gReggieConnected=false; + } +} + + +void QueryGameSearch() +{ + HashRef hashRef = HashInitialize(kHashFlagReggieKeys, "$KIND=redline $REDLINE$HOSTING=1"); + ReggieSearchBegin(gReggieRef,kReggieSearchGameChannel,kReggieRecordAdvertise,kReggieSearchFlagNotify,hashRef); /* Makes own copy of hash */ + HashDispose(hashRef); /* Not needed any more */ +} + +void QueryPlayerSearch() +{ + HashRef hashRef = HashInitialize(kHashFlagReggieKeys,"$KIND=redline $REDLINE$HOSTING=0"); + ReggieSearchBegin(gReggieRef,kReggieSearchPlayersChannel,kReggieRecordAdvertise,kReggieSearchFlagNotify,hashRef); + HashDispose(hashRef); /* Not needed any more */ +} + +void AdvertisePlayer() +{ + char hashString[1024]; + float idleAdd=TimeGetSeconds()-gStartIdleTime; + int reportIdle=(idleAdd>300); + gReportedIdle=reportIdle; + UInt32 startIdleLocalTime=TimerGetLocalSeconds()-idleAdd; + if(reportIdle) + sprintf(hashString,"$REDLINE$HOSTING=0 $TIME$IDLE=%d",startIdleLocalTime); + else + sprintf(hashString,"$REDLINE$HOSTING=0"); + HashRef hashRef = HashInitialize(kHashFlagReggieKeys,hashString); + char name[1024]; + sprintf(name,"%s%s",RT3_IsRegistered()?"":"\255demo.png\255 ",gConfig->playerName); + HashAppend(hashRef, "$NAME", name); + ReggieInsertBegin(gReggieRef,kReggieAdvGameChannel,kReggieRecordAdvertise,hashRef); + HashDispose(hashRef); +} + +void NetworkSearchGamesInit() +{ + gLastQuery=0; + QueryGameSearch(); + QueryPlayerSearch(); + AdvertisePlayer(); + //ReggieMessageBlock(gReggieRef,NULL,false); + gReggieSearching=true; +} + +int NetworkCountPlayers() +{ + static UInt32 lastPlayerStamp=-1; + static int lastCount=0; + if(gReggieConnected) + { + UInt32 state=ReggieGetStatus(gReggieRef,gMOTD,NULL); + if(!(state==kReggieStateRefused||state==kReggieStateOffline)) + { + if(!gReggieCounting) + { + HashRef hashRef = HashInitialize(kHashFlagReggieKeys,"$KIND=redline $REDLINE$HOSTING=0 COUNT"); + ReggieSearchBegin(gReggieRef,kReggieSearchPlayersChannel,kReggieRecordAdvertise,kReggieSearchFlagNotify,hashRef); + HashDispose(hashRef); /* Not needed any more */ + gReggieCounting=true; + } + + SInt32 reggieCount,totalCount; + UInt32 reggieStamp; + Bool8 outdated; + reggieStamp=ReggieSearchStatus(gReggieRef,kReggieSearchPlayersChannel,®gieCount,&totalCount,&outdated); + if(reggieStamp!=lastPlayerStamp) + { + if(outdated) + { + HashRef hashRef = HashInitialize(kHashFlagReggieKeys,"$KIND=redline $REDLINE$HOSTING=0 COUNT"); + ReggieSearchBegin(gReggieRef,kReggieSearchPlayersChannel,kReggieRecordAdvertise,kReggieSearchFlagNotify,hashRef); + HashDispose(hashRef); /* Not needed any more */ + } + lastPlayerStamp=reggieStamp; + } + if(reggieCount!=-1) + lastCount=totalCount; + return lastCount; + } + else + { + ReggieDispose(gReggieRef,true); + gReggieConnected=false; + } + } + else + TrackerConnect(); + return 0; +} + +void NetworkSearchGames(int *updated,tGameListEntry *games,int *numGames,int maxGames,tGameListPlayerEntry *players,int *numPlayers,int maxPlayers) +{ + static UInt32 lastGameStamp=-1,lastPlayerStamp=-1; + static float lastIdleAdd; + *updated=false; + + if(gReggieConnected) + { + UInt32 state=ReggieGetStatus(gReggieRef,gMOTD,NULL); + if(!(state==kReggieStateRefused||state==kReggieStateOffline)) + { + if(!gReggieSearching) + { + NetworkSearchGamesInit(); + lastGameStamp=-1; + lastPlayerStamp=-1; + gReportedIdle=0; + } + float idleAdd=TimeGetSeconds()-gStartIdleTime; + if(idleAdd>lastIdleAdd+15) + AdvertisePlayer(); + + lastIdleAdd=idleAdd; + int reportIdle=(idleAdd>300); + + if(reportIdle!=gReportedIdle) + AdvertisePlayer(); + SInt32 reggieCount,totalCount; + UInt32 reggieStamp; + Bool8 outdated; + reggieStamp=ReggieSearchStatus(gReggieRef,kReggieSearchGameChannel,®gieCount,&totalCount,&outdated); + if(reggieStamp!=lastGameStamp) + { + if(reggieCount==totalCount||reggieCount>*numGames) +// if(reggieCount>-1) + { + int oldNumGames=*numGames; + *numGames=0; + lastGameStamp=reggieStamp; + *updated=true; + NetworkAddress local; + DatagramGetAddress(NT_GetSessionEndpoint(gSessionRef),&local); + + int index=0; + for(;index0) + if(strcmp(games[*numGames].players[i].location,"Reserved Location")==0) + strcpy(games[*numGames].players[i].location,games[*numGames].players[0].location); + if(char *loc=strrchr(games[*numGames].players[i].location,',')) + memmove(games[*numGames].players[i].location,loc+1,strlen(loc)+1); + } + + + + (*numGames)++; + } + HashDispose(hashRef); + } + } + for(;index*numPlayers) + //if(reggieCount>-1) + { + int oldNumPlayers=*numPlayers; + *numPlayers=0; + lastPlayerStamp=reggieStamp; + *updated=true; + int index=0; + for(;index%d\n",reggieCount,totalCount,*numPlayers); + } + if(outdated&®gieCount==totalCount) + QueryPlayerSearch(); + } + } + else + { + NetworkSearchGamesStop(); + ReggieDispose(gReggieRef,true); + gReggieConnected=false; + } + } + else + { + *numGames=0; + TrackerConnect(); + } +} + +void NetworkSearchGamesStop() +{ + if(gReggieConnected) + { + //ReggieMessageBlock(gReggieRef,NULL,true); + ReggieChannelClose(gReggieRef,kReggieAdvGameChannel); + ReggieChannelClose(gReggieRef, kReggieSearchGameChannel); + ReggieChannelClose(gReggieRef, kReggieSearchPlayersChannel); + } + gReggieSearching=false; +} + +void NetworkCountPlayersStop() +{ + if(gReggieConnected) + { + ReggieChannelClose(gReggieRef, kReggieSearchPlayersChannel); + } + gReggieCounting=false; +} + + +int gWaitingForRegistry=false; +int gWaitingForRecord=false; + +void TrackerFetchRecord(tFileRef track,tFileRef car,int arcade,int reverse) +{ + if(gConfig->registerLapTimes) + { + if(!gReggieConnected) + if(!TrackerConnect()) + { + TextPrintfToBufferFormatedFading(Vector(0,-0.2),0.07,kTextAlignMiddle,1,2,"Unable to Connect lap time server"); + return; + } + + char hashString[512]; + HashRef hashRef; + tMapInfo *mapInfo=(tMapInfo*)FileGetParsedDataPtr(track,kParserTypeMapInfoDesc,sizeof(tMapInfo)); + if(!mapInfo->builtIn)return; + tCarDefinition *carDef=(tCarDefinition*)FileGetParsedDataPtr(car,kParserTypeCarDesc,sizeof(tCarDefinition)); + if(!carDef->builtIn)return; + + sprintf(hashString,"$REDLINE$MODE='%d' $REDLINE$REVERSE='%d'",arcade,reverse); + + hashRef = HashInitialize(kHashFlagReggieKeys, hashString); + HashAppend(hashRef, "$REDLINE$MAP", mapInfo->name); + HashAppend(hashRef, "$REDLINE$CAR", carDef->carName); + HashAppend(hashRef, "SORT", ">$REDLINE$TIME,>$TIME"); + HashAppend(hashRef, "LIMIT", "1"); + + //printf("hash:%s\n",(char*)(*HashSave(hashRef))); + ReggieSearchBegin(gReggieRef,kReggieRecordChannel,kReggieRecordHighScore,0,hashRef); + HashDispose(hashRef); + gWaitingForRecord=true; + + } +} + +void TrackerRegisterLapTime(tFileRef track,tFileRef car,int time,int arcade,int reverse) +{ + if(gConfig->registerLapTimes) + { + if(!gReggieConnected) + if(!TrackerConnect()) + { + TextPrintfToBufferFormatedFading(Vector(0,-0.2),0.07,kTextAlignMiddle,1,2,"Unable to Connect to lap time server"); + return; + } + char hashString[512]; + HashRef hashRef; + tMapInfo *mapInfo=(tMapInfo*)FileGetParsedDataPtr(track,kParserTypeMapInfoDesc,sizeof(tMapInfo)); + if(!mapInfo->builtIn)return; + tCarDefinition *carDef=(tCarDefinition*)FileGetParsedDataPtr(car,kParserTypeCarDesc,sizeof(tCarDefinition)); + if(!carDef->builtIn)return; + + sprintf(hashString,"$REDLINE$TIME='%d' $REDLINE$MODE='%d' $REDLINE$REVERSE='%d' $REDLINE$MAPCHECK='%8X' $REDLINE$CARCHECK='%8X'",time,arcade,reverse,FileGetChecksum(track),FileGetChecksum(car)); + hashRef = HashInitialize(kHashFlagReggieKeys, hashString); + HashAppend(hashRef, "$REDLINE$MAPFILE", FileGetName(track)); + HashAppend(hashRef, "$REDLINE$CARFILE", FileGetName(car)); + HashAppend(hashRef, "$REDLINE$MAP", mapInfo->name); + HashAppend(hashRef, "$REDLINE$CAR", carDef->carName); + HashAppend(hashRef, "$REDLINE$NAME", gConfig->playerName); + + ReggieInsertBegin(gReggieRef, kReggieInsertRecordChannel, kReggieRecordHighScore ,hashRef); + HashDispose(hashRef); + + sprintf(hashString,"$REDLINE$TIME<='%d' $REDLINE$TIME!='%d' $REDLINE$MODE='%d' $REDLINE$REVERSE='%d' COUNT",time,time,arcade,reverse); + hashRef = HashInitialize(kHashFlagReggieKeys, hashString); + HashAppend(hashRef, "$REDLINE$MAP", mapInfo->name); + HashAppend(hashRef, "$REDLINE$CAR", carDef->carName); + ReggieSearchBegin(gReggieRef,kReggieRegistryChannel,kReggieRecordHighScore,kReggieSearchFlagCount,hashRef); + HashDispose(hashRef); + gWaitingForRegistry=true; + } +} + +void TrackerLapTimeRegistryClose() +{ + if(gWaitingForRegistry||gWaitingForRecord) + { + ReggieChannelClose(gReggieRef,kReggieInsertRecordChannel); + ReggieChannelClose(gReggieRef,kReggieRegistryChannel); + ReggieChannelClose(gReggieRef,kReggieRecordChannel); + gWaitingForRegistry=gWaitingForRecord=false; + } +} + +void TrackerWaitForLapTimeRegistry() +{ + if(gWaitingForRegistry||gWaitingForRecord) + { + if(gWaitingForRegistry) + TextPrintfToBufferFormated(Vector(0,-0.2),0.05,kTextAlignMiddle,"Registering Lap Time"); + + UInt32 state=ReggieGetStatus(gReggieRef,gMOTD,NULL); + if(state==kReggieStateRefused||state==kReggieStateOffline) + { + if(state==kReggieStateRefused) + TextPrintfToBufferFormatedFading(Vector(0,-0.2),0.07,kTextAlignMiddle,1,2,"Connection Refused by lap time server"); + else + TextPrintfToBufferFormatedFading(Vector(0,-0.2),0.07,kTextAlignMiddle,1,2,"No response from lap time server"); + gWaitingForRegistry=false; + gWaitingForRecord=false; + gReggieConnected=false; + ReggieDispose(gReggieRef,false); + } + else + { + if(gWaitingForRegistry) + { + SInt32 reggieCount,totalCount; + ReggieSearchStatus(gReggieRef,kReggieRegistryChannel,®gieCount,&totalCount,NULL); + if(reggieCount!=-1) + { + if(totalCount==0) + TextPrintfToBufferFormatedFading(Vector(0,-0.2),0.1,kTextAlignMiddle,1,2,"World Record!!!"); + else if(totalCount<=25) + TextPrintfToBufferFormatedFading(Vector(0,-0.2),0.1,kTextAlignMiddle,1,2,"Place %d on World List",totalCount+1); + gWaitingForRegistry=false; + ReggieChannelClose(gReggieRef,kReggieRegistryChannel); + TrackerFetchRecord(gGameInfo->map,gGameInfo->playerCars[0],gGameInfo->arcade,gGameInfo->reverse^gMapInfo->reverse); + } + } + if(gWaitingForRecord) + { + SInt32 reggieCount,totalCount; + ReggieSearchStatus(gReggieRef,kReggieRecordChannel,®gieCount,&totalCount,NULL); + if(reggieCount!=-1) + { + if(totalCount==0) + { + gWorldRecord=0; + gWaitingForRecord=false; + } + else if(reggieCount>0){ + HashRef hashRef = ReggieSearchIndex(gReggieRef,kReggieRecordChannel,0); + //printf("$REDLINE$TIME%s\n",HashLookup(hashRef,"$REDLINE$TIME",nil)); + //printf("$REDLINE$NAME%s\n",HashLookup(hashRef,"$REDLINE$NAME",nil)); + //printf("hash:%s\n",(char*)(*HashSave(hashRef))); + sscanf(HashLookup(hashRef,"$REDLINE$TIME",nil),"%d",&gWorldRecord); + strcpy(gRecordName,HashLookup(hashRef,"$REDLINE$NAME",nil)); + + int numChars=0,index=0; + while(numChars<12) + { + if(gRecordName[index]=='\255') + while(gRecordName[++index]!='\255'); + index++; + numChars++; + } + gRecordName[index]='\0'; + gWaitingForRecord=false; + ReggieChannelClose(gReggieRef,kReggieRecordChannel); + + } + } + } + } + } +} \ No newline at end of file diff --git a/source/tracker.h b/source/tracker.h new file mode 100644 index 0000000..fd4ac90 --- /dev/null +++ b/source/tracker.h @@ -0,0 +1,63 @@ +#ifndef __TRACKER +#define __TRACKER + +#include "gameinitexit.h" +#include "network.h" + +typedef struct{ + char name[64]; + char car[64]; + char location[256]; + UInt32 startIdleTime; +} tGameListPlayerEntry; + +enum{ + kJoinStateOK=0, + kJoinStateReggie, + kJoinStateFail +}; + +typedef struct{ + char host[32]; + char alias[32]; + char map[32]; + char name[256]; + int loaded; + int password; + int numPlayers; + int numNetPlayers; + int maxPlayers; + int arcade; + int locked; + int joinState; + int onlyRegistered; + tFileRef mapRef; + tGameListPlayerEntry players[kMaxPlayers]; +} tGameListEntry; + +extern int gWaitingForRegistry; +extern int gWaitingForRecord; + +void NetworkStopAdvertising(); +void NetworkAdvertiseIdle(tGameInfo *gInfo,int *showLonelyMessage,int locked,int forceUpdate); + +//void NetworkSearchGames(tGameListEntry *games,int *numGames,int maxGames,int *updated,int *numPlayers); +void NetworkSearchGames(int *updated,tGameListEntry *games,int *numGames,int maxGames,tGameListPlayerEntry *players,int *numPlayers,int maxPlayers); +void NetworkSearchGamesStop(); + +int NetworkCountPlayers(); +void NetworkCountPlayersStop(); + +void TrackerFetchRecord(tFileRef track,tFileRef car,int arcade,int reverse); +void TrackerRegisterLapTime(tFileRef track,tFileRef car,int time,int arcade,int reverse); +void TrackerWaitForLapTimeRegistry(); +void TrackerLapTimeRegistryClose(); + +void TrackerMessageSend(char *message); +int TrackerMessageReceive(tChatMessage *msg); + +void TrackerStartVersionCheck(); +int TrackerVersionCheck(char **newVersion); +void TrackerGetNewVersion(); + +#endif \ No newline at end of file diff --git a/source/tracks.cpp b/source/tracks.cpp new file mode 100644 index 0000000..4293271 --- /dev/null +++ b/source/tracks.cpp @@ -0,0 +1,138 @@ +//tracks.cpp +//draw tire tracks on the road + +#include +#include "textures.h" +#include "fileio.h" +#include "vectors.h" +#include "renderframe.h" +#include "gamemem.h" +#include "network.h" +#include "tracks.h" +#include "log.h" +#include "error.h" +#include "gameinitexit.h" +#include "environment.h" + +#define kMaxTrack 512 //size of track segment buffer +#define kTrackShiftSize 256 //how many track segmnents to delete when buffer is full + +#define kTrackHeight 0.02 //how far tracks are drawn above ground (to avoid z-buffer problems) +#define kMaxIntensity 0.8 +#define kTrackTexScale 0.18 + +typedef struct{ + tVector3 pos1l,pos1r,pos2l,pos2r,normal; + int texture; + float l1,l2; + float intensity1,intensity2; +} tTrackSegment; + +tTrackSegment gTrack[kMaxTrack];//track segment buffer +int gNumTracks=0; +int gTrackIndexShift=0; + +//Add a new segment of tire tracks, going from pos1 to pos2 +//attach specifies a track segment which links to this one +int TracksAdd(tVector3 pos1,tVector3 pos2,tVector3 normal,float width,int texture,float intensity,int attach) +{ + //is there any space left in the track segment buffer? + if(gNumTracks>=kMaxTrack) + { + //if not, delete kTrackShiftSize elements, by shifting the track segment buffer + gTrackIndexShift+=kTrackShiftSize; + gNumTracks-=kTrackShiftSize; + MemoryMove(gTrack,gTrack+kTrackShiftSize,(kMaxTrack-kTrackShiftSize)*sizeof(tTrackSegment)); + } + + float adjustedIntensity=intensity*kMaxIntensity; + pos1.y+=kTrackHeight;//to avoid Z-Buffer Problems + pos2.y+=kTrackHeight; + + tVector3 crossNormal=!((pos1-pos2)%normal); + + gTrack[gNumTracks].pos1l=pos1+crossNormal*width*0.5; + gTrack[gNumTracks].pos1r=pos1-crossNormal*width*0.5; + gTrack[gNumTracks].pos2l=pos2+crossNormal*width*0.5; + gTrack[gNumTracks].pos2r=pos2-crossNormal*width*0.5; + + gTrack[gNumTracks].normal=normal; + + gTrack[gNumTracks].texture=texture; + + gTrack[gNumTracks].intensity1=adjustedIntensity; + gTrack[gNumTracks].intensity2=adjustedIntensity; + gTrack[gNumTracks].l1=0; + gTrack[gNumTracks].l2=~(pos2-pos1); + + + //does attach correspods to a given Track Segment in the Buffer? + if(attach-gTrackIndexShift>0) + { + //fix the Track Segments to properly fit together. + gTrack[attach-gTrackIndexShift].pos2l=gTrack[gNumTracks].pos1l; + gTrack[attach-gTrackIndexShift].pos2r=gTrack[gNumTracks].pos1r; + gTrack[attach-gTrackIndexShift].l2=gTrack[attach-gTrackIndexShift].l1+~(gTrack[attach-gTrackIndexShift].pos2l-gTrack[attach-gTrackIndexShift].pos1l); + gTrack[attach-gTrackIndexShift].intensity2=adjustedIntensity; + float lOffs=gTrack[attach-gTrackIndexShift].l2; + gTrack[gNumTracks].l1+=lOffs; + gTrack[gNumTracks].l2+=lOffs; + } + return (gNumTracks++)+gTrackIndexShift; +} + +//clear the Track Segment Buffer +void TracksClear() +{ + gNumTracks=0; +} + +//Render the Tracks +void TracksRender(tVector3 *clipPlanes) +{ + SetupWorldTranslation(); + + glPushAttrib(GL_COLOR_BUFFER_BIT+GL_CURRENT_BIT+GL_DEPTH_BUFFER_BIT+GL_LIGHTING_BIT); + glDisable(GL_LIGHTING); + glEnable(GL_BLEND); + glDepthMask(false); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + int tex=-1; + for(int i=0;iambient.x+gEnvironment->ambient.y+gEnvironment->ambient.z)*0.33; + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2d(0,gTrack[i].l1*kTrackTexScale); + glColor4f(light,light,light,gTrack[i].intensity1); + glVertex3fv(&(gTrack[i].pos1r).x); + glTexCoord2d(0,gTrack[i].l2*kTrackTexScale); + glColor4f(light,light,light,gTrack[i].intensity2); + glVertex3fv(&(gTrack[i].pos2r).x); + glTexCoord2d(1,gTrack[i].l1*kTrackTexScale); + glColor4f(light,light,light,gTrack[i].intensity1); + glVertex3fv(&(gTrack[i].pos1l).x); + glTexCoord2d(1,gTrack[i].l2*kTrackTexScale); + glColor4f(light,light,light,gTrack[i].intensity2); + glVertex3fv(&(gTrack[i].pos2l).x); + glEnd(); + #ifdef __POLYCOUNT + gPolyCount+=2; + #endif + } + glPopAttrib(); +} \ No newline at end of file diff --git a/source/tracks.h b/source/tracks.h new file mode 100644 index 0000000..132156a --- /dev/null +++ b/source/tracks.h @@ -0,0 +1 @@ +#ifndef __TRACKS #define __TRACKS int TracksAdd(tVector3 pos1,tVector3 pos2,tVector3 normal,float width,int texture,float intensity,int attach); void TracksRender(tVector3 *clipPoints); void TracksClear(); #endif \ No newline at end of file diff --git a/source/transparency.cpp b/source/transparency.cpp new file mode 100644 index 0000000..717210c --- /dev/null +++ b/source/transparency.cpp @@ -0,0 +1,612 @@ +//transparency.cpp +//sort and draw all transparent polygons + +#include +#include +#include +#include "fileio.h" +#include "vectors.h" +#include "environment.h" +#include "transparency.h" +#include "entities.h" +#include "textures.h" +#include "renderframe.h" +#include "config.h" +#include "gameframe.h" +#include "gameinitexit.h" +#include "text.h" +#include "sky.h" + +//an index used to sort transparent polygons +typedef struct{ + float distance; + int poly; +}tTransparentPolyDistSort; + + +float gTunnelFactor=0; +float gBlurMapFactor=0; +float gGlowFactor=0; +float gShineFactor=1; +int gGhostShine=false; + +#define kMaxTransparentPolys 16384 //maximum number of transparent polygons in scene +#define kMaxMaterialStorage 1024 +tPolyMaterial *gMaterialStorage; +tTransparentPoly *gTranparentPolys; //transparent poly buffer +tTransparentPolyDistSort *gPolyDistance; //transparent poly index +int gTransparentPolyCount=0; //transparent polygons currently used +int gMaterialStorageCount=0; + +void InitTransparency() +{ + gMaterialStorage=(tPolyMaterial*)malloc(sizeof(tPolyMaterial)*kMaxMaterialStorage); + gTranparentPolys=(tTransparentPoly*)malloc(sizeof(tTransparentPoly)*kMaxTransparentPolys); + gPolyDistance=(tTransparentPolyDistSort*)malloc(sizeof(tTransparentPolyDistSort)*kMaxTransparentPolys); +} + +//returns a pointer to an unused tTransparentPoly which needs to be filled with data +//and increments the transparent polygon count +tTransparentPoly* GetNextTransparentPoly(int allocateMaterial) +{ + if(gTransparentPolyCountspecular; + *(tVector3*)ambientReflectance=mat->ambient; + *(tVector3*)diffuseReflectance=mat->diffuse; + + specularReflectance[3]=1; + ambientReflectance[3]=1; + diffuseReflectance[3]=1; + + glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,diffuseReflectance); + glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT,ambientReflectance); + glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,specularReflectance); + glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,mat->shininess); + if(mat->flags&kMaterialEnableGlow) + { + emission[0]=gGlowFactor; + emission[1]=gGlowFactor*0.3; + } + if(mat->flags&kMaterialEnableShine) + { + emission[0]=gShineFactor*gEnvironment->spotLightColor.x; + emission[1]=gShineFactor*gEnvironment->spotLightColor.y; + emission[2]=gShineFactor*gEnvironment->spotLightColor.z; + } + if(gGhostShine) + { + emission[0]=1.5; + emission[1]=1.5; + emission[2]=1.5; + } + glMaterialfv(GL_FRONT_AND_BACK,GL_EMISSION,emission); + if(mat->flags&kMaterialDisableLighting) + glDisable(GL_LIGHTING); + else + glEnable(GL_LIGHTING); + + TexturesSelectTex(FileGetIndexedReference(mat->texRef,textureSet)); +/* glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + if(mat->flags&kMaterialMoveUVs) + glTranslatef(kFrameTime*gFrameCount,kFrameTime*gFrameCount,0); + glMatrixMode(GL_MODELVIEW);*/ + + if(mat->flags&kMaterialDisableWrap) + { + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); + } + else if(mat->flags&kMaterialDisableWrapS) + { + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT); + } + else if(mat->flags&kMaterialDisableWrapT) + { + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); + } + else + { + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT); + } + + + if(mat->flags&kMaterialBothSides) + glDisable(GL_CULL_FACE); + else + glEnable(GL_CULL_FACE); + + + if(gEnvironment->environmentMapping) + if(mat->flags&kMaterialSphereMap){ + if(TexturesSelectTextureUnit(1)) + { + #ifndef __TARGET_TOOLAPP + + if(gMapEnv) + if(gMapEnv->spheremap) + TexturesSelectTex(gMapEnv->spheremap); + else + TexturesSelectTex(gEnvironment->spheremap); + else + #endif + TexturesSelectTex(gEnvironment->spheremap); + glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_SPHERE_MAP); + glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_SPHERE_MAP); + glEnable(GL_TEXTURE_GEN_S); + glEnable(GL_TEXTURE_GEN_T); + glMatrixMode(GL_TEXTURE); + tMatrix4 m; + MatrixCopy(gTransformDir,m); + + glLoadMatrixf((float*)m); + glMatrixMode(GL_MODELVIEW); + if(gEnvironment->environmentMapping==kEnvironmentMappingSphereInterpolate) + { +#define GL_MODULATE_SIGNED_ADD_ATI 0x8745 +#define GL_MODULATE_ADD_ATI 0x8744 + /* GLfloat color[4]={1,1,1,gEnvironment->environmentMapIntensity*gEnvironmentMapFactor}; + glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE_ARB); + glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB_ARB,GL_INTERPOLATE_ARB); + glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE2_RGB_ARB,GL_CONSTANT_ARB); + glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND2_RGB_ARB,GL_SRC_ALPHA); + glTexEnvfv(GL_TEXTURE_ENV,GL_TEXTURE_ENV_COLOR,color); */ + GLfloat color[4]={1,1,1,0.5}; + glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE_ARB); + glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB_ARB,GL_MODULATE_ADD_ATI); + //glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB_ARB,GL_INTERPOLATE_ARB); + glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE0_RGB_ARB,GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND0_RGB_ARB,GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE1_RGB_ARB,GL_PREVIOUS_ARB); + glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND1_RGB_ARB,GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE2_RGB_ARB,GL_PREVIOUS_ARB); + glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND2_RGB_ARB,GL_SRC_ALPHA); + + //glTexEnvfv(GL_TEXTURE_ENV,GL_TEXTURE_ENV_COLOR,color); + } + else + { + glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE_ARB); + glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB_ARB,GL_ADD_SIGNED_ARB); + } + TexturesSelectTextureUnit(0); + } + } + else { + if(TexturesSelectTextureUnit(1)){ + glDisable(GL_TEXTURE_2D); + glDisable(GL_TEXTURE_3D); + glDisable(GL_TEXTURE_GEN_S); + glDisable(GL_TEXTURE_GEN_T); + TexturesSelectTextureUnit(0); + } + } + +#ifndef __TARGET_TOOLAPP + int enableLightMap=false; + if(gMapEnv) + if(gMapEnv->lightMap) + if((!gMapEnv->lightMap2)||((mat->flags&kMaterialSphereMap)&&!gTunnelFactor)) + if(TexturesSelectTextureUnit(2)) + { + TexturesSelectTex(gMapEnv->lightMap); + glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE_ARB); + glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB_ARB,GL_MODULATE); + + glEnable(GL_TEXTURE_GEN_S); + glEnable(GL_TEXTURE_GEN_T); + glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR); + glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR); + if(gMapEnv->lightMapBotRight.x!=gMapEnv->lightMapTopLeft.x) + { + GLfloat xPlane[]={1.0f/(gMapEnv->lightMapBotRight.x-gMapEnv->lightMapTopLeft.x),0.0f,0.0f,(gTransformPos.x-gMapEnv->lightMapTopLeft.x+gFrameCount*kFrameTime*gMapEnv->lightMapSpeed.x)/(gMapEnv->lightMapBotRight.x-gMapEnv->lightMapTopLeft.x)}; + glTexGenfv(GL_S,GL_OBJECT_PLANE,xPlane); + } + if(gMapEnv->lightMapBotRight.y!=gMapEnv->lightMapTopLeft.y) + { + GLfloat yPlane[]={0.0f,0.0f,1.0f/(gMapEnv->lightMapBotRight.y-gMapEnv->lightMapTopLeft.y),(gTransformPos.z-gMapEnv->lightMapTopLeft.y+gFrameCount*kFrameTime*gMapEnv->lightMapSpeed.y)/(gMapEnv->lightMapBotRight.y-gMapEnv->lightMapTopLeft.y)}; + glTexGenfv(GL_T,GL_OBJECT_PLANE,yPlane); + } + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + enableLightMap=true; + TexturesSelectTextureUnit(0); + } + if(!enableLightMap) + if(TexturesSelectTextureUnit(2)) + { + glDisable(GL_TEXTURE_2D); + TexturesSelectTextureUnit(0); + } + +#endif + + if(allowTransparency) + { + if(mat->flags&kMaterialUseAlphaChannel) + { + //glColor4f(1,1,1,0.5); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + // glEnable(GL_ALPHA_TEST); + // glAlphaFunc(GL_GREATER,0.0); + } + else{ + // glDisable(GL_ALPHA_TEST); + glDisable(GL_BLEND); + } + if(mat->flags&kMaterialAlphaTest) + { + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER,0.5); + } + else + glDisable(GL_ALPHA_TEST); + } + if(TexturesSelectTextureUnit(3)) + { + glDisable(GL_TEXTURE_2D); + TexturesSelectTextureUnit(0); + } + return mat->flags&kMaterialUseAlphaChannel; +} + +//setup OpenGL to use the properties of the material passed in mat +void UseMaterial2(tPolyMaterial *mat,tPolyMaterial *mat2,int textureSet) +{ + GLfloat specularReflectance[4]; + GLfloat ambientReflectance[4]; + GLfloat diffuseReflectance[4]; + GLfloat emission[4]={0,0,0,1}; + + *(tVector3*)specularReflectance=mat->specular; + *(tVector3*)ambientReflectance=mat->ambient; + *(tVector3*)diffuseReflectance=mat->diffuse; + + specularReflectance[3]=1; + ambientReflectance[3]=1; + diffuseReflectance[3]=1; + + glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,diffuseReflectance); + glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT,ambientReflectance); + glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,specularReflectance); + glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,mat->shininess); + if(mat->flags&kMaterialEnableGlow) + { + emission[0]=gGlowFactor; + emission[1]=gGlowFactor*0.3; + } + if(mat->flags&kMaterialEnableShine) + { + emission[0]=gShineFactor*gEnvironment->spotLightColor.x; + emission[1]=gShineFactor*gEnvironment->spotLightColor.y; + emission[2]=gShineFactor*gEnvironment->spotLightColor.z; + } + glMaterialfv(GL_FRONT_AND_BACK,GL_EMISSION,emission); + if(mat->flags&kMaterialDisableLighting) + glDisable(GL_LIGHTING); + else + glEnable(GL_LIGHTING); + + TexturesSelectTex(FileGetIndexedReference(mat->texRef,textureSet)); + + if(mat->flags&kMaterialDisableWrap) + { + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); + } + else if(mat->flags&kMaterialDisableWrapS) + { + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT); + } + else if(mat->flags&kMaterialDisableWrapT) + { + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); + } + else + { + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT); + } + + if(mat->flags&kMaterialBothSides) + glDisable(GL_CULL_FACE); + else + glEnable(GL_CULL_FACE); + + + if(gConfig->stencil){ + if(TexturesSelectTextureUnit(1)) + { + glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE_ARB); + glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB_ARB,GL_MODULATE); + glDisable(GL_TEXTURE_GEN_S); + glDisable(GL_TEXTURE_GEN_T); + glMatrixMode(GL_TEXTURE); + glEnable(GL_TEXTURE_2D); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + if(!gLightning) + { + TexturesSelectTex(FileGetIndexedReference(mat2->texRef,gEnvironment->envFlags)); + if(mat2->flags&kMaterialDisableWrap) + { + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); + } + else + { + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT); + } + } + else + TexturesSelectTex(FileGetReference("null.raw")); + TexturesSelectTextureUnit(0); + } + } + else + { + glEnable(GL_LIGHTING); + if(TexturesSelectTextureUnit(1)) + { + glDisable(GL_TEXTURE_2D); + TexturesSelectTextureUnit(0); + } + } + +#ifndef __TARGET_TOOLAPP + if(gMapEnv) + if(gMapEnv->lightMap2) + { + if(TexturesSelectTextureUnit(2)) + { + if(!gLightning) + TexturesSelectTex(gMapEnv->lightMap2); + else + TexturesSelectTex(FileGetReference("null.raw")); + glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE_ARB); + glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB_ARB,GL_MODULATE); + glEnable(GL_TEXTURE_GEN_S); + glEnable(GL_TEXTURE_GEN_T); + glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR); + glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR); + GLfloat xPlane[]={1/(gMapEnv->lightMap2BotRight.x-gMapEnv->lightMap2TopLeft.x),0,0,(gTransformPos.x-gMapEnv->lightMap2TopLeft.x+gFrameCount*kFrameTime*gMapEnv->lightMap2Speed.x)/(gMapEnv->lightMap2BotRight.x-gMapEnv->lightMap2TopLeft.x)}; + glTexGenfv(GL_S,GL_OBJECT_PLANE,xPlane); + GLfloat yPlane[]={0,0,1/(gMapEnv->lightMap2BotRight.y-gMapEnv->lightMap2TopLeft.y),(gTransformPos.z-gMapEnv->lightMap2TopLeft.y+gFrameCount*kFrameTime*gMapEnv->lightMap2Speed.y)/(gMapEnv->lightMap2BotRight.y-gMapEnv->lightMap2TopLeft.y)}; + glTexGenfv(GL_T,GL_OBJECT_PLANE,yPlane); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + TexturesSelectTextureUnit(0); + } + } + else if(gMapEnv->lightMap) + if(TexturesSelectTextureUnit(2)) + { + if(!gLightning) + TexturesSelectTex(gMapEnv->lightMap); + else + TexturesSelectTex(FileGetReference("null.raw")); + glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE_ARB); + glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB_ARB,GL_MODULATE); + glEnable(GL_TEXTURE_GEN_S); + glEnable(GL_TEXTURE_GEN_T); + glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR); + glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR); + GLfloat xPlane[]={1/(gMapEnv->lightMapBotRight.x-gMapEnv->lightMapTopLeft.x),0,0,(gTransformPos.x-gMapEnv->lightMapTopLeft.x+gFrameCount*kFrameTime*gMapEnv->lightMapSpeed.x)/(gMapEnv->lightMapBotRight.x-gMapEnv->lightMapTopLeft.x)}; + glTexGenfv(GL_S,GL_OBJECT_PLANE,xPlane); + GLfloat yPlane[]={0,0,1/(gMapEnv->lightMapBotRight.y-gMapEnv->lightMapTopLeft.y),(gTransformPos.z-gMapEnv->lightMapTopLeft.y+gFrameCount*kFrameTime*gMapEnv->lightMapSpeed.y)/(gMapEnv->lightMapBotRight.y-gMapEnv->lightMapTopLeft.y)}; + glTexGenfv(GL_T,GL_OBJECT_PLANE,yPlane); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + TexturesSelectTextureUnit(0); + } +#endif + if(mat->flags&kMaterialSphereMap&&gEnvironment->particlesAmount){ + if(TexturesSelectTextureUnit(3)) + { + TexturesSelectTex(FileGetReference("rainreflection.pct")); + glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_SPHERE_MAP); + glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_SPHERE_MAP); + glEnable(GL_TEXTURE_GEN_S); + glEnable(GL_TEXTURE_GEN_T); + glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE_ARB); + glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB_ARB,GL_INTERPOLATE_ARB); + glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE2_RGB_ARB,GL_PREVIOUS_ARB); + glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND2_RGB_ARB,GL_SRC_ALPHA); + + TexturesSelectTextureUnit(0); + } + } else if(TexturesSelectTextureUnit(3)) + { + glDisable(GL_TEXTURE_2D); + TexturesSelectTextureUnit(0); + } +} + +int UseGhostMaterial(tPolyMaterial *mat,int textureSet) +{ + GLfloat specularReflectance[4]; + GLfloat ambientReflectance[4]; + GLfloat diffuseReflectance[4]; + + *(tVector3*)specularReflectance=mat->specular; + *(tVector3*)ambientReflectance=mat->ambient; + *(tVector3*)diffuseReflectance=mat->diffuse; + + specularReflectance[3]=1; + ambientReflectance[3]=1; + diffuseReflectance[3]=1; + + glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,diffuseReflectance); + glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT,ambientReflectance); + glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,specularReflectance); + + + glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,mat->shininess); + + TexturesSelectTex(FileGetIndexedReference(mat->texRef,textureSet)); + + if(mat->flags&kMaterialDisableWrap) + { + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP); + } + else if(mat->flags&kMaterialDisableWrapS) + { + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT); + } + else if(mat->flags&kMaterialDisableWrapT) + { + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP); + } + else + { + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT); + } + + glDisable(GL_CULL_FACE); + + glActiveTextureARB(GL_TEXTURE1_ARB); + glDisable(GL_TEXTURE_2D); + glActiveTextureARB(GL_TEXTURE0_ARB); + + glEnable(GL_BLEND); + glBlendFunc(GL_ONE,GL_ONE); + + return true; +} + +//compare function used to sort the polygon index by the Z-Distance from the camera +int PolyTableCompare(tTransparentPolyDistSort *a,tTransparentPolyDistSort *b) +{ + float diff=(a->distance)-(b->distance); + if(diff==0) return 0; + else return sign(diff); +} + +//calculates the z-distance from the camera for each polygon index value +void PolyTableDepthSetup(tVector3 *clipPlanes) +{ + tVector3 camZ=-*MatrixGetZVector(gCameraEntity->dir); + for(int i=0;iv[0].vertex+p->v[1].vertex+p->v[2].vertex)*(1.0f/3.0f); + + if(clipPlanes) + if(ClipPointDistanced(clipPlanes,¢er,20.0f)) + gPolyDistance[i].distance=-10000.0f; + else + gPolyDistance[i].distance=(center-gCameraEntity->pos)*camZ; + else + gPolyDistance[i].distance=(center-gCameraEntity->pos)*camZ; + gPolyDistance[i].poly=i; + } +} + +void DrawTransparentPolys(tVector3 *clipPlanes) +{ + glPushAttrib(GL_LIGHTING_BIT+GL_TEXTURE_BIT+GL_POLYGON_BIT+GL_COLOR_BUFFER_BIT+GL_DEPTH_BUFFER_BIT); + + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + + PolyTableDepthSetup(clipPlanes); + SetupWorldTranslation(); + //depth-sort the polygons + qsort(gPolyDistance,gTransparentPolyCount,sizeof(tTransparentPolyDistSort),(int (*)(const void *, const void *))&PolyTableCompare); + + //draw all the polygons + int texture=-1; + int textureSet=-1; + int ghost=0; + int i=0; + + for(;itexRef||textureSet!=gTranparentPolys[polyIndex].textureSet||ghost!=gTranparentPolys[polyIndex].ghost||gGlowFactor!=gTranparentPolys[polyIndex].glow) + { + gGlowFactor=gTranparentPolys[polyIndex].glow; + if(gTranparentPolys[polyIndex].ghost) + UseGhostMaterial(gTranparentPolys[polyIndex].mat,gTranparentPolys[polyIndex].textureSet); + else + UseMaterial(gTranparentPolys[polyIndex].mat,true,gTranparentPolys[polyIndex].textureSet); + + glDepthMask(!(gTranparentPolys[polyIndex].mat->flags&kMaterialDisableShadow)); + texture=gTranparentPolys[polyIndex].mat->texRef; + textureSet=gTranparentPolys[polyIndex].textureSet; + ghost=gTranparentPolys[polyIndex].ghost; + } + + glBegin(GL_TRIANGLES); + for(int v=0;v<3;v++) + { + glNormal3fv(&gTranparentPolys[polyIndex].v[v].normal.x); + glTexCoord3f(gTranparentPolys[polyIndex].v[v].texel.x,gTranparentPolys[polyIndex].v[v].texel.y,0.25+0.5*gTranparentPolys[polyIndex].blur); + // glTexCoord3f(gTranparentPolys[polyIndex].v[v].texel.x,gTranparentPolys[polyIndex].v[v].texel.y,0.75); + glVertex3fv(&gTranparentPolys[polyIndex].v[v].vertex.x); + } + glEnd(); + } + + //reset transparent polygon count + gTransparentPolyCount=0; + gMaterialStorageCount=0; + + glDisable(GL_BLEND); + if(TexturesSelectTextureUnit(1)){ + glDisable(GL_TEXTURE_2D); + glDisable(GL_TEXTURE_3D); + glDisable(GL_TEXTURE_GEN_S); + glDisable(GL_TEXTURE_GEN_T); + TexturesSelectTextureUnit(0); + } + + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + + glPopAttrib(); +} \ No newline at end of file diff --git a/source/transparency.h b/source/transparency.h new file mode 100644 index 0000000..21ef7d2 --- /dev/null +++ b/source/transparency.h @@ -0,0 +1,60 @@ +#ifndef __TRANSPARENCY +#define __TRANSPARENCY + +#include "vectors.h" +#include "fileio.h" + +enum{ + kMaterialBothSides=1<<0, + kMaterialUseAlphaChannel=1<<1, + kMaterialSphereMap=1<<2, + kMaterialDisableWrap=1<<3, + kMaterialAlphaTest=1<<4, + kMaterialDisableShadow=1<<5, + kMaterialDisableWrapS=1<<6, + kMaterialDisableWrapT=1<<7, + kMaterialEnableGlow=1<<8, + //kMaterialBlurMap=1<<9, + kMaterialEnableShine=1<<10, + kMaterialDisableLighting=1<<11, + kMaterialMoveUVs=1<<12 +}; + +typedef struct{ + int numTextures; + tFileRef *texRefs; +} tTexRefRecord; + +typedef struct{ + tFileRef texRef; + int flags; + float shininess; + tVector3 specular,ambient,diffuse; +}tPolyMaterial; + +typedef struct{ + tVector2 texel; + tVector3 normal,vertex; + tVector2 texel2; +} tVertexArrayVertex; + +typedef tVertexArrayVertex tVertexArrayElement[3]; + +typedef struct{ + tVertexArrayElement v; + int textureSet; + int ghost; + float glow,blur; + tPolyMaterial *mat; +}tTransparentPoly; + +extern float gTunnelFactor,gGlowFactor,gShineFactor,gBlurMapFactor; +extern int gGhostShine; + +void InitTransparency(); +void DrawTransparentPolys(tVector3 *clipPlanes); +tTransparentPoly* GetNextTransparentPoly(int allocateMaterial); +int UseMaterial(tPolyMaterial *mat,int allowTransparency,int textureSet); +void UseMaterial2(tPolyMaterial *mat,tPolyMaterial *mat2,int textureSet); + +#endif \ No newline at end of file diff --git a/source/vectors.cpp b/source/vectors.cpp new file mode 100755 index 0000000..dde1ade --- /dev/null +++ b/source/vectors.cpp @@ -0,0 +1,541 @@ +//vectors.cpp +//vector and matrix math functions + +#include "vectors.h" +#include "gamemem.h" +#include +//Add and Subtract Matrices +void MatrixAdd(tMatrix3 x,tMatrix3 y,tMatrix3 m) +{ + int i,j; + for(i=0;i<3;i++) + for(j=0;j<3;j++) + m[i][j]=x[i][j]+y[i][j]; +} + +void MatrixAdd(tMatrix4 x,tMatrix4 y,tMatrix4 m) +{ + int i,j; + for(i=0;i<4;i++) + for(j=0;j<4;j++) + m[i][j]=x[i][j]+y[i][j]; +} + +void MatrixSub(tMatrix3 x,tMatrix3 y,tMatrix3 m) +{ + int i,j; + for(i=0;i<3;i++) + for(j=0;j<3;j++) + m[i][j]=x[i][j]-y[i][j]; +} + +void MatrixSub(tMatrix4 x,tMatrix4 y,tMatrix4 m) +{ + int i,j; + for(i=0;i<4;i++) + for(j=0;j<4;j++) + m[i][j]=x[i][j]-y[i][j]; +} + +//Multiplay Matrices with Scalars +void MatrixMult(tMatrix3 x,float y,tMatrix3 m) +{ + int i,j; + for(i=0;i<3;i++) + for(j=0;j<3;j++) + m[i][j]=x[i][j]*y; +} + +void MatrixMult(tMatrix4 x,float y,tMatrix4 m) +{ + int i,j; + for(i=0;i<4;i++) + for(j=0;j<4;j++) + m[i][j]=x[i][j]*y; +} + +void MatrixMult(float y,tMatrix3 x,tMatrix3 m) +{ + int i,j; + for(i=0;i<3;i++) + for(j=0;j<3;j++) + m[i][j]=x[i][j]*y; +} + +void MatrixMult(float y,tMatrix4 x,tMatrix4 m) +{ + int i,j; + for(i=0;i<4;i++) + for(j=0;j<4;j++) + m[i][j]=x[i][j]*y; +} + +//Convert a 3x3 matrix to a 4x4 matrix +void Matrix3ToMatrix4(tMatrix3 x,tMatrix4 m) +{ + m[0][0]=x[0][0];m[0][1]=x[0][1];m[0][2]=x[0][2];m[0][3]=0; + m[1][0]=x[1][0];m[1][1]=x[1][1];m[1][2]=x[1][2];m[1][3]=0; + m[2][0]=x[2][0];m[2][1]=x[2][1];m[2][2]=x[2][2];m[2][3]=0; + m[3][0]=0; m[3][1]=0; m[3][2]=0; m[3][3]=1; +} + +//Copy matrices +void MatrixCopy(tMatrix3 src,tMatrix3 dst) +{ + MemoryMove(dst,src,sizeof(tMatrix3)); +} + +void MatrixCopy(tMatrix4 src,tMatrix4 dst) +{ + MemoryMove(dst,src,sizeof(tMatrix4)); +} + +void MatrixCopy(tMatrix3 src,tMatrix4 dst) +{ + Matrix3ToMatrix4(src,dst); +} + +//Convert a vector of euler angles to a matrix +void EulerAnglesToMatrix(tVector3 euler,tMatrix3 dst) +{ + MatrixIdentity(dst); + MatrixRotX(dst,euler.x); + MatrixRotY(dst,euler.y); + MatrixRotZ(dst,euler.z); +} + +void EulerAnglesToMatrix(tVector3 euler,tMatrix4 dst) +{ + MatrixIdentity(dst); + MatrixRotX(dst,euler.x); + MatrixRotY(dst,euler.y); + MatrixRotZ(dst,euler.z); +} + +//Convert a matrix to a vector of euler angles +tVector3 MatrixToEulerAngles(tMatrix3 m) +{ + tVector3 euler; + if(fabs(m[0][2])<=1) + euler.y=-asin(m[0][2]); + else + euler.y=-asin(sign(m[0][2])); + float C=cos(euler.y); + float trX,trY; + if(fabs(C)>0.005) + { + trX=m[2][2]/C; + trY=-m[1][2]/C; + + euler.x=-atan2(trY,trX); + + trX=m[0][0]/C; + trY=-m[0][1]/C; + + euler.z=-atan2(trY,trX); + } + else //Gimbal Lock + { + euler.x=0; + + trX=m[1][1]; + trY=m[1][0]; + + euler.z=atan2(trY,trX); + } + return euler; +} + +tVector3 MatrixToEulerAngles(tMatrix4 m) +{ + tVector3 euler; + euler.y=-asin(m[0][2]); + float C=cos(euler.y); + float trX,trY; + if(fabs(C)>0.005) + { + trX=m[2][2]/C; + trY=-m[1][2]/C; + + euler.x=-atan2(trY,trX); + + trX=m[0][0]/C; + trY=-m[0][1]/C; + + euler.z=-atan2(trY,trX); + } + else //Gimbal Lock + { + euler.x=0; + + trX=m[1][1]; + trY=m[1][0]; + + euler.z=atan2(trY,trX); + } + return euler; +} + +//Multiply matrices +void MatrixMult(tMatrix3 x,tMatrix3 y,tMatrix3 m) +{ + int i,j,k; + if(x==m||y==m){ + tMatrix3 target; + for(j=0;j<3;j++) + { + target[0][j]=x[0][0]*y[0][j] + +x[0][1]*y[1][j] + +x[0][2]*y[2][j]; + target[1][j]=x[1][0]*y[0][j] + +x[1][1]*y[1][j] + +x[1][2]*y[2][j]; + target[2][j]=x[2][0]*y[0][j] + +x[2][1]*y[1][j] + +x[2][2]*y[2][j]; + } + MemoryMove(m,target,sizeof(tMatrix3)); + } + else + for(j=0;j<3;j++) + { + m[0][j]=x[0][0]*y[0][j] + +x[0][1]*y[1][j] + +x[0][2]*y[2][j]; + m[1][j]=x[1][0]*y[0][j] + +x[1][1]*y[1][j] + +x[1][2]*y[2][j]; + m[2][j]=x[2][0]*y[0][j] + +x[2][1]*y[1][j] + +x[2][2]*y[2][j]; + } +} + +void MatrixMult(tMatrix3 x,tMatrix4 y,tMatrix4 m) +{ + tMatrix4 x4; + Matrix3ToMatrix4(x,x4); + MatrixMult(x4,y,m); +} + +void MatrixMult(tMatrix4 x,tMatrix3 y,tMatrix4 m) +{ + tMatrix4 y4; + Matrix3ToMatrix4(y,y4); + MatrixMult(x,y4,m); +} + +void MatrixMult(tMatrix4 x,tMatrix4 y,tMatrix4 m) +{ + int i,j,k; + if(x==m||y==m){ + tMatrix4 target; + for(j=0;j<4;j++) + { + target[0][j]=x[0][0]*y[0][j] + +x[0][1]*y[1][j] + +x[0][2]*y[2][j] + +x[0][3]*y[3][j]; + target[1][j]=x[1][0]*y[0][j] + +x[1][1]*y[1][j] + +x[1][2]*y[2][j] + +x[1][3]*y[3][j]; + target[2][j]=x[2][0]*y[0][j] + +x[2][1]*y[1][j] + +x[2][2]*y[2][j] + +x[2][3]*y[3][j]; + target[3][j]=x[3][0]*y[0][j] + +x[3][1]*y[1][j] + +x[3][2]*y[2][j] + +x[3][3]*y[3][j]; + } + MemoryMove(m,target,sizeof(tMatrix4)); + } + else + { + for(j=0;j<4;j++) + { + m[0][j]=x[0][0]*y[0][j] + +x[0][1]*y[1][j] + +x[0][2]*y[2][j] + +x[0][3]*y[3][j]; + m[1][j]=x[1][0]*y[0][j] + +x[1][1]*y[1][j] + +x[1][2]*y[2][j] + +x[1][3]*y[3][j]; + m[2][j]=x[2][0]*y[0][j] + +x[2][1]*y[1][j] + +x[2][2]*y[2][j] + +x[2][3]*y[3][j]; + m[3][j]=x[3][0]*y[0][j] + +x[3][1]*y[1][j] + +x[3][2]*y[2][j] + +x[3][3]*y[3][j]; + } + } +} + +//makes an identity matrix +void MatrixIdentity(tMatrix3 m) +{ + m[0][0]=1;m[0][1]=0;m[0][2]=0; + m[1][0]=0;m[1][1]=1;m[1][2]=0; + m[2][0]=0;m[2][1]=0;m[2][2]=1; +} + +void MatrixIdentity(tMatrix4 m) +{ + m[0][0]=1;m[0][1]=0;m[0][2]=0;m[0][3]=0; + m[1][0]=0;m[1][1]=1;m[1][2]=0;m[1][3]=0; + m[2][0]=0;m[2][1]=0;m[2][2]=1;m[2][3]=0; + m[3][0]=0;m[3][1]=0;m[3][2]=0;m[3][3]=1; +} + +//Translates a matrix +void MatrixTranslate(tMatrix4 target,float x,float y,float z) +{ + tMatrix4 m; + m[0][0]=1;m[0][1]=0;m[0][2]=0;m[0][3]=0; + m[1][0]=0;m[1][1]=1;m[1][2]=0;m[1][3]=0; + m[2][0]=0;m[2][1]=0;m[2][2]=1;m[2][3]=0; + m[3][0]=x;m[3][1]=y;m[3][2]=z;m[3][3]=1; + MatrixMult(target,m,target); +} + +void MatrixTranslateVector(tMatrix4 target,tVector3 v) +{ + tMatrix4 m; + m[0][0]=1;m[0][1]=0;m[0][2]=0;m[0][3]=0; + m[1][0]=0;m[1][1]=1;m[1][2]=0;m[1][3]=0; + m[2][0]=0;m[2][1]=0;m[2][2]=1;m[2][3]=0; + m[3][0]=v.x;m[3][1]=v.y;m[3][2]=v.z;m[3][3]=1; + MatrixMult(target,m,target); +} + +//Scales a matrix +void MatrixScale(tMatrix3 target,float x,float y,float z) +{ + tMatrix3 m; + m[0][0]=x;m[0][1]=0;m[0][2]=0; + m[1][0]=0;m[1][1]=y;m[1][2]=0; + m[2][0]=0;m[2][1]=0;m[2][2]=z; + MatrixMult(target,m,target); +} + +void MatrixScale(tMatrix4 target,float x,float y,float z) +{ + tMatrix4 m; + m[0][0]=x;m[0][1]=0;m[0][2]=0;m[0][3]=0; + m[1][0]=0;m[1][1]=y;m[1][2]=0;m[1][3]=0; + m[2][0]=0;m[2][1]=0;m[2][2]=z;m[2][3]=0; + m[3][0]=0;m[3][1]=0;m[3][2]=0;m[3][3]=1; + MatrixMult(target,m,target); +} + +void MatrixScaleVector(tMatrix3 target,tVector3 v) +{ + tMatrix3 m; + m[0][0]=v.x;m[0][1]=0;m[0][2]=0; + m[1][0]=0;m[1][1]=v.y;m[1][2]=0; + m[2][0]=0;m[2][1]=0;m[2][2]=v.z; + MatrixMult(target,m,target); +} + +void MatrixScaleVector(tMatrix4 target,tVector3 v) +{ + tMatrix4 m; + m[0][0]=v.x;m[0][1]=0;m[0][2]=0;m[0][3]=0; + m[1][0]=0;m[1][1]=v.y;m[1][2]=0;m[1][3]=0; + m[2][0]=0;m[2][1]=0;m[2][2]=v.z;m[2][3]=0; + m[3][0]=0;m[3][1]=0;m[3][2]=0;m[3][3]=1; + MatrixMult(target,m,target); +} + +//Rotates a matrix +void MatrixRotX(tMatrix3 target,float r) +{ + tMatrix3 m; + m[0][0]=1;m[0][1]=0;m[0][2]=0; + m[1][0]=0;m[1][1]=cos(r);m[1][2]=sin(r); + m[2][0]=0;m[2][1]=-sin(r);m[2][2]=cos(r); + MatrixMult(target,m,target); +} + +void MatrixRotX(tMatrix4 target,float r) +{ + tMatrix4 m; + m[0][0]=1;m[0][1]=0;m[0][2]=0;m[0][3]=0; + m[1][0]=0;m[1][1]=cos(r);m[1][2]=sin(r);m[1][3]=0; + m[2][0]=0;m[2][1]=-sin(r);m[2][2]=cos(r);m[2][3]=0; + m[3][0]=0;m[3][1]=0;m[3][2]=0;m[3][3]=1; + MatrixMult(target,m,target); +} + +void MatrixRotY(tMatrix3 target,float r) +{ + tMatrix3 m; + m[0][0]=cos(r);m[0][1]=0;m[0][2]=-sin(r); + m[1][0]=0;m[1][1]=1;m[1][2]=0; + m[2][0]=sin(r);m[2][1]=0;m[2][2]=cos(r); + MatrixMult(target,m,target); +} + +void MatrixRotY(tMatrix4 target,float r) +{ + tMatrix4 m; + m[0][0]=cos(r);m[0][1]=0;m[0][2]=-sin(r);m[0][3]=0; + m[1][0]=0;m[1][1]=1;m[1][2]=0;m[1][3]=0; + m[2][0]=sin(r);m[2][1]=0;m[2][2]=cos(r);m[2][3]=0; + m[3][0]=0;m[3][1]=0;m[3][2]=0;m[3][3]=1; + MatrixMult(target,m,target); +} + +void MatrixRotZ(tMatrix3 target,float r) +{ + tMatrix3 m; + m[0][0]=cos(r);m[0][1]=sin(r);m[0][2]=0; + m[1][0]=-sin(r);m[1][1]=cos(r);m[1][2]=0; + m[2][0]=0;m[2][1]=0;m[2][2]=1; + MatrixMult(target,m,target); +} + +void MatrixRotZ(tMatrix4 target,float r) +{ + tMatrix4 m; + m[0][0]=cos(r);m[0][1]=sin(r);m[0][2]=0;m[0][3]=0; + m[1][0]=-sin(r);m[1][1]=cos(r);m[1][2]=0;m[1][3]=0; + m[2][0]=0;m[2][1]=0;m[2][2]=1;m[2][3]=0; + m[3][0]=0;m[3][1]=0;m[3][2]=0;m[3][3]=1; + MatrixMult(target,m,target); +} + +//Transposes a matrix +void MatrixTranspose(tMatrix3 x,tMatrix3 dst) +{ + int i,j; + for(i=0;i<3;i++) + for(j=0;j<3;j++) + dst[i][j]=x[j][i]; +} + +void MatrixTranspose(tMatrix4 x,tMatrix4 dst) +{ + int i,j; + for(i=0;i<4;i++) + for(j=0;j<4;j++) + dst[i][j]=x[j][i]; +} + +//Verifies if a matrix is a valid rotation matrix (Orthonormal base). +int MatrixVerify(tMatrix3 m) +{ + tVector3 *m0=(tVector3*)m[0]; + float len=m0->x*m0->x+m0->y*m0->y+m0->z*m0->z; + if(len>=1+kMatrixTolerance||len<=1-kMatrixTolerance)return false; + tVector3 v=(*(tVector3*)m[1]%*(tVector3*)m[2])-*(tVector3*)m[0]; + return(v.x*v.x+v.y*v.y+v.z*v.z<=kMatrixTolerance); +} + +int MatrixVerify(tMatrix4 m) +{ + tVector3 v=(*(tVector3*)m[1]%*(tVector3*)m[2])-*(tVector3*)m[0]; + return(v.x*v.x+v.y*v.y+v.z*v.z<=kMatrixTolerance); +} + + +//Readjusts matrices to be a valid rotation matrix +//needed as floating point calculations always carry small errors, so matrices +//will drift apart more and more +void MatrixReAdjust(tMatrix3 m) +{ + *(tVector3*)m[2]=!*(tVector3*)m[2]; + *(tVector3*)m[0]=!(*(tVector3*)m[1]%*(tVector3*)m[2]); + *(tVector3*)m[1]=!(*(tVector3*)m[2]%*(tVector3*)m[0]); +} + +void MatrixReAdjust(tMatrix4 m) +{ + *(tVector3*)m[2]=!*(tVector3*)m[2]; + *(tVector3*)m[0]=!(*(tVector3*)m[1]%*(tVector3*)m[2]); + *(tVector3*)m[1]=!(*(tVector3*)m[2]%*(tVector3*)m[0]); +} + +//converts a rotation vector to a rotation matrix. +//a rotation vector defines a rotation by the length of a vector +//around the axis given by the direction of a vector. +void RotationVectorToMatrix(tVector3 axis,tMatrix4 mat) +{ + float angle=~axis; + if(!angle) + { + MatrixIdentity(mat); + return; + } + else axis=axis/angle; + tMatrix3 axist; + int i,j; + for(i=0;i<3;i++) + for(j=0;j<3;j++) + axist[i][j]=((float*)&axis)[i]*((float*)&axis)[j]; + tMatrix3 m1,m2,s; + s[0][0]=0; s[0][1]=-axis.z; s[0][2]=axis.y; + s[1][0]=axis.z; s[1][1]=0; s[1][2]=-axis.x; + s[2][0]=-axis.y;s[2][1]=axis.x; s[2][2]=0; + MatrixIdentity(m1); + MatrixSub(m1,axist,m1); + MatrixMult(m1,cos(-angle),m1); + MatrixAdd(m1,axist,m1); + MatrixMult(s,sin(-angle),m2); + MatrixAdd(m1,m2,m1); + Matrix3ToMatrix4(m1,mat); +} + +void RotationVectorToMatrix(tVector3 axis,tMatrix3 mat) +{ + float angle=~axis; + if(!angle) + { + MatrixIdentity(mat); + return; + } + else axis=axis/angle; + tMatrix3 axist; + int i,j; + for(i=0;i<3;i++) + for(j=0;j<3;j++) + axist[i][j]=((float*)&axis)[i]*((float*)&axis)[j]; + tMatrix3 m1,m2,s; + s[0][0]=0; s[0][1]=-axis.z; s[0][2]=axis.y; + s[1][0]=axis.z; s[1][1]=0; s[1][2]=-axis.x; + s[2][0]=-axis.y;s[2][1]=axis.x; s[2][2]=0; + MatrixIdentity(m1); + MatrixSub(m1,axist,m1); + MatrixMult(m1,cos(-angle),m1); + MatrixAdd(m1,axist,m1); + MatrixMult(s,sin(-angle),m2); + MatrixAdd(m1,m2,mat); +} + + +//tests if two vectors are equal +int VectorEqual(tVector3 a,tVector3 b) +{ + return(a.x==b.x&&a.y==b.y&&a.z==b.z); +} + +int VectorEqual(tVector2 a,tVector2 b) +{ + return(a.x==b.x&&a.y==b.y); +} + +//tests if a vector is zero +int VectorZero(tVector2 v) +{ + return v.x==0&&v.y==0; +} + +int VectorZero(tVector3 v) +{ + return v.x==0&&v.y==0&&v.z==0; +} + diff --git a/source/vectors.h b/source/vectors.h new file mode 100755 index 0000000..6c254e8 --- /dev/null +++ b/source/vectors.h @@ -0,0 +1,305 @@ +#ifndef __VECTORS +#define __VECTORS + +typedef struct{ + float x,y; +} tVector2; + +typedef struct{ + float x,y,z; +} tVector3; + +typedef float tMatrix3[3][3]; +typedef float tMatrix4[4][4]; + +#ifndef PI +#define PI 3.14159265359 +#endif + +#define kDegreeRadians (2*PI/360.0) + +#define kMatrixTolerance 0.001 + +#define MatrixGetXVector(m) ((tVector3*)(m)[0]) +#define MatrixGetYVector(m) ((tVector3*)(m)[1]) +#define MatrixGetZVector(m) ((tVector3*)(m)[2]) +#define MatrixGetPosVector(m) ((tVector3*)(m)[3]) + +void MatrixAdd(tMatrix3 x, tMatrix3 y, tMatrix3 m); +void MatrixAdd(tMatrix4 x, tMatrix4 y, tMatrix4 m); +void MatrixSub(tMatrix3 x, tMatrix3 y, tMatrix3 m); +void MatrixSub(tMatrix4 x, tMatrix4 y, tMatrix4 m); +void MatrixCopy(tMatrix3 src,tMatrix3 dst); +void MatrixCopy(tMatrix4 src,tMatrix4 dst); +void MatrixCopy(tMatrix3 src,tMatrix4 dst); +void MatrixMult(tMatrix3 x, tMatrix3 y, tMatrix3 m); +void MatrixMult(tMatrix3 x, tMatrix4 y, tMatrix4 m); +void MatrixMult(tMatrix4 x, tMatrix3 y, tMatrix4 m); +void MatrixMult(tMatrix4 x, tMatrix4 y, tMatrix4 m); +void MatrixIdentity(tMatrix3 m); +void MatrixIdentity(tMatrix4 m); +void MatrixTranslate(tMatrix4 target, float x, float y, float z); +void MatrixTranslateVector(tMatrix4 target,tVector3 v); +void MatrixScale(tMatrix3 target, float x, float y, float z); +void MatrixScale(tMatrix4 target, float x, float y, float z); +void MatrixScaleVector(tMatrix3 target,tVector3 v); +void MatrixScaleVector(tMatrix4 target,tVector3 v); +void MatrixRotX(tMatrix3 target, float r); +void MatrixRotX(tMatrix4 target, float r); +void MatrixRotY(tMatrix3 target, float r); +void MatrixRotY(tMatrix4 target, float r); +void MatrixRotZ(tMatrix3 target, float r); +void MatrixRotZ(tMatrix4 target, float r); +void MatrixTranspose(tMatrix3 x,tMatrix3 dst); +void MatrixTranspose(tMatrix4 x,tMatrix4 dst); +int MatrixVerify(tMatrix3 m); +int MatrixVerify(tMatrix4 m); +void MatrixReAdjust(tMatrix3 m); +void MatrixReAdjust(tMatrix4 m); +void RotationVectorToMatrix(tVector3 axis,tMatrix3 mat); +void RotationVectorToMatrix(tVector3 axis,tMatrix4 mat); +void EulerAnglesToMatrix(tVector3 euler,tMatrix3 dst); +void EulerAnglesToMatrix(tVector3 euler,tMatrix4 dst); +tVector3 MatrixToEulerAngles(tMatrix3 m); +tVector3 MatrixToEulerAngles(tMatrix4 m); + +//returns the vector (x,y) +inline tVector2 Vector(float x,float y) +{ + tVector2 v; + v.x=x; + v.y=y; + return v; +} + +//returns the vector (x,y,z) +inline tVector3 Vector(float x,float y,float z) +{ + tVector3 v; + v.x=x; + v.y=y; + v.z=z; + return v; +} + +int VectorZero(tVector2 v); +int VectorZero(tVector3 v); +int VectorEqual(tVector3 a,tVector3 b); +int VectorEqual(tVector2 a,tVector2 b); + +//add two vectors +inline tVector2 operator +(tVector2 a,tVector2 b) +{ + tVector2 v; + v.x=a.x+b.x; + v.y=a.y+b.y; + return v; +} + +inline tVector3 operator +(tVector3 a,tVector3 b) +{ + tVector3 v; + v.x=a.x+b.x; + v.y=a.y+b.y; + v.z=a.z+b.z; + return v; +} + +//subtracts two vectors +inline tVector2 operator -(tVector2 a,tVector2 b) +{ + tVector2 v; + v.x=a.x-b.x; + v.y=a.y-b.y; + return v; +} + +inline tVector3 operator -(tVector3 a,tVector3 b) +{ + tVector3 v; + v.x=a.x-b.x; + v.y=a.y-b.y; + v.z=a.z-b.z; + return v; +} + +//returns the additive inverse of a vector +inline tVector2 operator -(tVector2 v) +{ + tVector2 w; + w.x=-v.x; + w.y=-v.y; + return w; +} + +inline tVector3 operator -(tVector3 v) +{ + tVector3 w; + w.x=-v.x; + w.y=-v.y; + w.z=-v.z; + return w; +} + +inline tVector2 operator *(float s, tVector2 v) +{ + tVector2 w; + w.x=v.x*s; + w.y=v.y*s; + return w; +} + +inline tVector3 operator *(float s, tVector3 v) +{ + tVector3 w; + w.x=v.x*s; + w.y=v.y*s; + w.z=v.z*s; + return w; +} + +inline tVector2 operator *(tVector2 v,float s) +{ + tVector2 w; + w.x=v.x*s; + w.y=v.y*s; + return w; +} + +inline tVector3 operator *(tVector3 v,float s) +{ + tVector3 w; + w.x=v.x*s; + w.y=v.y*s; + w.z=v.z*s; + return w; +} + +/* +tVector3 operator *(tVector3 v,tMatrix4 m); +tVector3 operator *(tVector3 v,tMatrix3 m); +tVector3 operator *(tMatrix4 m,tVector3 v); +tVector3 operator *(tMatrix3 m,tVector3 v); +tVector2 operator /(tVector2 v,float s); +tVector3 operator /(tVector3 v,float s); +*/ +//the dot product of two vectors +inline float operator *(tVector2 a,tVector2 b) +{ + return a.x*b.x+a.y*b.y; +} + +inline float operator *(tVector3 a,tVector3 b) +{ + return a.x*b.x+a.y*b.y+a.z*b.z; +} + +//i know the overloading operators with operations that do not relate +//to the operator's original meaning is considered bad programming. +//however, imho, once one has adopted these conventions, complex vector +//code becomes much more readable. + +//the cross product of two vectors +inline tVector3 operator %(tVector3 a,tVector3 b) +{ + return Vector( + a.y*b.z-b.y*a.z, + a.z*b.x-b.z*a.x, + a.x*b.y-b.x*a.y + ); +} + +//the absolute value of a vector + +inline float operator ~(tVector2 v) +{ + return sqrt(v.x*v.x+v.y*v.y); +} + +inline float operator ~(tVector3 v) +{ + return sqrt(v.x*v.x+v.y*v.y+v.z*v.z); +} + +//the normalized form of a vector +inline tVector2 operator !(tVector2 v) +{ + float val=~v; + return val>0.0?((1/val)*v):Vector(0,0); +} + +inline tVector3 operator !(tVector3 v) +{ + float val=~v; + return val>0.0?((1/val)*v):Vector(0,0,0); +} + +//x*x +inline float sqr(float x) +{ + return x*x; +} + +inline float sqr(tVector2 x) +{ + return x*x; +} + +inline float sqr(tVector3 x) +{ + return x*x; +} + +//multiplies a vector by a matrix +inline tVector3 operator *(tVector3 v,tMatrix4 m) +{ + return Vector( + v.x*m[0][0]+v.y*m[1][0]+v.z*m[2][0]+m[3][0], + v.x*m[0][1]+v.y*m[1][1]+v.z*m[2][1]+m[3][1], + v.x*m[0][2]+v.y*m[1][2]+v.z*m[2][2]+m[3][2] + ); +} + +inline tVector3 operator *(tVector3 v,tMatrix3 m) +{ + return Vector( + v.x*m[0][0]+v.y*m[1][0]+v.z*m[2][0], + v.x*m[0][1]+v.y*m[1][1]+v.z*m[2][1], + v.x*m[0][2]+v.y*m[1][2]+v.z*m[2][2] + ); +} + +inline tVector3 operator *(tMatrix4 m,tVector3 v) +{ + return Vector( + v.x*m[0][0]+v.y*m[1][0]+v.z*m[2][0]+m[3][0], + v.x*m[0][1]+v.y*m[1][1]+v.z*m[2][1]+m[3][1], + v.x*m[0][2]+v.y*m[1][2]+v.z*m[2][2]+m[3][2] + ); +} + +inline tVector3 operator *(tMatrix3 m,tVector3 v) +{ + return Vector( + v.x*m[0][0]+v.y*m[1][0]+v.z*m[2][0], + v.x*m[0][1]+v.y*m[1][1]+v.z*m[2][1], + v.x*m[0][2]+v.y*m[1][2]+v.z*m[2][2] + ); +} + +//multiplies a vector by 1/scalar +inline tVector2 operator /(tVector2 v,float s) +{ + return Vector(v.x/s,v.y/s); +} + +inline tVector3 operator /(tVector3 v,float s) +{ + return Vector(v.x/s,v.y/s,v.z/s); +} + + + +#define sign(x) ((x)<0?-1:1) + +#endif \ No newline at end of file diff --git a/source/writeout.cpp b/source/writeout.cpp new file mode 100644 index 0000000..c5fda2b --- /dev/null +++ b/source/writeout.cpp @@ -0,0 +1,340 @@ +#include +#include +#include "fileio.h" +#include "parser.h" +#include "entities.h" +#include "gameinitexit.h" +#include "gamemem.h" +#include "config.h" +#include "textures.h" + +void FailWithErrorString(char *string); + +void WriteOutFileName(char *buffer,int *bufferPos,char *label,tFileRef fileID) +{ + char line[256]; + if(fileID!=-1) + sprintf(line,"%s \"%s\"\n",label,FileGetName(fileID)); + else + sprintf(line,"%s \"\"\n",label); + int len=strlen(line); + if(buffer) + memcpy(buffer+*bufferPos,line,len); + *bufferPos+=len; +} + +void WriteOutVector3(char *buffer,int *bufferPos,char *label,tVector3 v) +{ + char line[256]; + sprintf(line,"%s {%f,%f,%f}\n",label,v.x,v.y,v.z); + int len=strlen(line); + if(buffer) + memcpy(buffer+*bufferPos,line,len); + *bufferPos+=len; +} + +void WriteOutVector2(char *buffer,int *bufferPos,char *label,tVector2 v) +{ + char line[256]; + sprintf(line,"%s {%f,%f}\n",label,v.x,v.y); + int len=strlen(line); + if(buffer) + memcpy(buffer+*bufferPos,line,len); + *bufferPos+=len; +} + +void WriteOutInt(char *buffer,int *bufferPos,char *label,int arrayPos) +{ + if(arrayPos) + { + char line[256]; + sprintf(line,"%s %d\n",label,arrayPos); + int len=strlen(line); + if(buffer) + memcpy(buffer+*bufferPos,line,len); + *bufferPos+=len; + } +} + +void WriteOutFloat(char *buffer,int *bufferPos,char *label,float x) +{ + if(x!=0.0) + { + char line[256]; + sprintf(line,"%s %f\n",label,x); + int len=strlen(line); + if(buffer) + memcpy(buffer+*bufferPos,line,len); + *bufferPos+=len; + } +} + +void WriteOutHex(char *buffer,int *bufferPos,char *label,int arrayPos) +{ + if(arrayPos) + { + char line[256]; + sprintf(line,"%s 0x%x\n",label,arrayPos); + int len=strlen(line); + if(buffer) + memcpy(buffer+*bufferPos,line,len); + *bufferPos+=len; + } +} + +void WriteOutString(char *buffer,int *bufferPos,char *label,char *str) +{ + if(str[0]) + { + char line[256]; + sprintf(line,"%s \"%s\"\n",label,str); + int len=strlen(line); + if(buffer) + memcpy(buffer+*bufferPos,line,len); + *bufferPos+=len; + } +} + +void WriteOutArrayPos(char *buffer,int *bufferPos,int arrayPos) +{ + char line[256]; + sprintf(line,"# %d\n",arrayPos); + int len=strlen(line); + if(buffer) + memcpy(buffer+*bufferPos,line,len); + *bufferPos+=len; +} + +void WriteOutArrayPos2(char *buffer,int *bufferPos,int arrayPos) +{ + char line[256]; + sprintf(line,"## %d\n",arrayPos); + int len=strlen(line); + if(buffer) + memcpy(buffer+*bufferPos,line,len); + *bufferPos+=len; +} + +int WriteOutMapInfo(char *buffer,int *bufferPos,tFileRef fileID,tMapInfo *mapInfo) +{ + WriteOutFileName(buffer,bufferPos,"road",mapInfo->road); + WriteOutFileName(buffer,bufferPos,"image",mapInfo->image); + WriteOutVector3(buffer,bufferPos,"startPos",mapInfo->startPos); + WriteOutVector3(buffer,bufferPos,"finishPos",mapInfo->finishPos); + + WriteOutVector2(buffer,bufferPos,"overviewTopLeft",mapInfo->overviewTopLeft); + WriteOutVector2(buffer,bufferPos,"overviewBotRight",mapInfo->overviewBotRight); + + WriteOutInt(buffer,bufferPos,"loop",mapInfo->loop); + WriteOutInt(buffer,bufferPos,"reverse",mapInfo->reverse); + WriteOutInt(buffer,bufferPos,"useEnts",mapInfo->demoAvailable); + WriteOutInt(buffer,bufferPos,"hasOverview",mapInfo->hasOverview); + WriteOutInt(buffer,bufferPos,"hideMap",mapInfo->hideMap); + WriteOutInt(buffer,bufferPos,"dontDrawRoad",mapInfo->dontDrawRoad); + WriteOutInt(buffer,bufferPos,"builtIn",mapInfo->builtIn); + WriteOutInt(buffer,bufferPos,"maxPlayers",mapInfo->maxPlayers); + WriteOutInt(buffer,bufferPos,"playerPos",mapInfo->playerPos); + WriteOutInt(buffer,bufferPos,"dirtEnable",mapInfo->dirtEnable); + WriteOutInt(buffer,bufferPos,"baseSurfaceType",mapInfo->baseSurfaceType); + WriteOutFloat(buffer,bufferPos,"startLineOffset",mapInfo->startLineOffset); + WriteOutFloat(buffer,bufferPos,"startCenterOffset",mapInfo->startCenterOffset); + WriteOutFloat(buffer,bufferPos,"carOffset",mapInfo->carOffset); + WriteOutFloat(buffer,bufferPos,"speedFactor",mapInfo->speedFactor); + WriteOutFloat(buffer,bufferPos,"dirtIntensity",mapInfo->dirtIntensity); + + WriteOutFileName(buffer,bufferPos,"roadTypes",mapInfo->roadTypes); + WriteOutFileName(buffer,bufferPos,"overview",mapInfo->overview); + + WriteOutString(buffer,bufferPos,"name",mapInfo->name); + WriteOutFileName(buffer,bufferPos,"dirtMap",mapInfo->dirtMap); + WriteOutInt(buffer,bufferPos,"numObjs",mapInfo->numObjs); + for(int arrayPos=0;arrayPosnumObjs;arrayPos++) + { + WriteOutArrayPos(buffer,bufferPos,arrayPos); + WriteOutFileName(buffer,bufferPos,"obj.model",mapInfo->obj[arrayPos].model); + WriteOutVector3(buffer,bufferPos,"obj.pos",mapInfo->obj[arrayPos].pos); + WriteOutVector3(buffer,bufferPos,"obj.dir",mapInfo->obj[arrayPos].dir); + WriteOutInt(buffer,bufferPos,"obj.color",mapInfo->obj[arrayPos].color); + WriteOutInt(buffer,bufferPos,"obj.untouchable",mapInfo->obj[arrayPos].untouchable); + WriteOutHex(buffer,bufferPos,"obj.envFlags",mapInfo->obj[arrayPos].envFlags); + } + WriteOutInt(buffer,bufferPos,"numVisWalls",mapInfo->numVisWalls); + for(int arrayPos=0;arrayPosnumVisWalls;arrayPos++) + { + WriteOutArrayPos(buffer,bufferPos,arrayPos); + WriteOutVector3(buffer,bufferPos,"a",mapInfo->visWalls[arrayPos].a); + WriteOutVector3(buffer,bufferPos,"b",mapInfo->visWalls[arrayPos].b); + WriteOutVector3(buffer,bufferPos,"c",mapInfo->visWalls[arrayPos].c); + } + WriteOutInt(buffer,bufferPos,"numMapEnvs",mapInfo->numMapEnvs); + for(int arrayPos=0;arrayPosnumMapEnvs;arrayPos++) + { + WriteOutArrayPos(buffer,bufferPos,arrayPos); + WriteOutHex(buffer,bufferPos,"mapEnv.envFlags",mapInfo->mapEnv[arrayPos].envFlags); + WriteOutInt(buffer,bufferPos,"mapEnv.lightEnable",mapInfo->mapEnv[arrayPos].lightEnable); + WriteOutFloat(buffer,bufferPos,"mapEnv.fogBegin",mapInfo->mapEnv[arrayPos].fogBegin); + WriteOutFloat(buffer,bufferPos,"mapEnv.fogEnd",mapInfo->mapEnv[arrayPos].fogEnd); + WriteOutVector3(buffer,bufferPos,"mapEnv.fogColor",mapInfo->mapEnv[arrayPos].fogColor); + WriteOutFileName(buffer,bufferPos,"mapEnv.sky0",mapInfo->mapEnv[arrayPos].sky0); + WriteOutFileName(buffer,bufferPos,"mapEnv.sky90",mapInfo->mapEnv[arrayPos].sky90); + WriteOutFileName(buffer,bufferPos,"mapEnv.sky180",mapInfo->mapEnv[arrayPos].sky180); + WriteOutFileName(buffer,bufferPos,"mapEnv.sky270",mapInfo->mapEnv[arrayPos].sky270); + WriteOutFileName(buffer,bufferPos,"mapEnv.skytop",mapInfo->mapEnv[arrayPos].skytop); + WriteOutFileName(buffer,bufferPos,"mapEnv.skybot",mapInfo->mapEnv[arrayPos].skybot); + WriteOutFileName(buffer,bufferPos,"mapEnv.lightMap",mapInfo->mapEnv[arrayPos].lightMap); + WriteOutFileName(buffer,bufferPos,"mapEnv.spheremap",mapInfo->mapEnv[arrayPos].spheremap); + WriteOutVector2(buffer,bufferPos,"mapEnv.lightMapTopLeft",mapInfo->mapEnv[arrayPos].lightMapTopLeft); + WriteOutVector2(buffer,bufferPos,"mapEnv.lightMapBotRight",mapInfo->mapEnv[arrayPos].lightMapBotRight); + WriteOutVector3(buffer,bufferPos,"mapEnv.lightDir",mapInfo->mapEnv[arrayPos].lightDir); + WriteOutVector3(buffer,bufferPos,"mapEnv.flareDir",mapInfo->mapEnv[arrayPos].flareDir); + } +} + +int WriteOutConfig(char *buffer,int *bufferPos,tFileRef fileID,tSysConfig *config) +{ + WriteOutInt(buffer,bufferPos,"screenXSize",config->screenXSize); + WriteOutInt(buffer,bufferPos,"screenYSize",config->screenYSize); + WriteOutInt(buffer,bufferPos,"windowX",config->windowX); + WriteOutInt(buffer,bufferPos,"windowY",config->windowY); + WriteOutInt(buffer,bufferPos,"allCams",config->allCams); + WriteOutInt(buffer,bufferPos,"useBetaBuilds",config->useBetaBuilds); + WriteOutInt(buffer,bufferPos,"noGhost",config->noGhost); + WriteOutInt(buffer,bufferPos,"onlyRegisteredPlayers",config->onlyRegisteredPlayers); + WriteOutInt(buffer,bufferPos,"cantGoBackwards",config->cantGoBackwards); + WriteOutInt(buffer,bufferPos,"fullscreen",config->fullscreen); + WriteOutInt(buffer,bufferPos,"performanceStats",config->performanceStats); + WriteOutInt(buffer,bufferPos,"stencil",config->stencil); + WriteOutInt(buffer,bufferPos,"textureQuality",config->textureQuality); + WriteOutInt(buffer,bufferPos,"textureFilter",config->textureFilter); + WriteOutInt(buffer,bufferPos,"soundEnable",config->soundEnable); + WriteOutInt(buffer,bufferPos,"maxCarSources",config->maxCarSources); + WriteOutInt(buffer,bufferPos,"fsaa",config->fsaa); + WriteOutInt(buffer,bufferPos,"dbIndex",config->dbIndex); + WriteOutInt(buffer,bufferPos,"arcade",config->arcade); + WriteOutInt(buffer,bufferPos,"reverse",config->reverse); + WriteOutInt(buffer,bufferPos,"ffb",config->ffb); + WriteOutInt(buffer,bufferPos,"color32Bit",config->color32Bit); + WriteOutInt(buffer,bufferPos,"metricUnits",config->metricUnits); + WriteOutInt(buffer,bufferPos,"interiorDisplay",config->interiorDisplay); + WriteOutInt(buffer,bufferPos,"cameraMode",config->cameraMode); + WriteOutInt(buffer,bufferPos,"guideSigns",config->guideSigns); + WriteOutInt(buffer,bufferPos,"carsOnSpeed",config->carsOnSpeed); + WriteOutInt(buffer,bufferPos,"demolition",config->demolition); + WriteOutInt(buffer,bufferPos,"trackerEnable",config->trackerEnable); + WriteOutInt(buffer,bufferPos,"maxPlayers",config->maxPlayers); + WriteOutInt(buffer,bufferPos,"registerLapTimes",config->registerLapTimes); + WriteOutInt(buffer,bufferPos,"showPlayerNames",config->showPlayerNames); + WriteOutInt(buffer,bufferPos,"motionBlur",config->motionBlur); + WriteOutInt(buffer,bufferPos,"showReplays",config->showReplays); + WriteOutInt(buffer,bufferPos,"allowHugeGames",config->allowHugeGames); + WriteOutInt(buffer,bufferPos,"interfaceSounds",config->interfaceSounds); + WriteOutHex(buffer,bufferPos,"challengeData",config->challengeData); + WriteOutInt(buffer,bufferPos,"seperateGasBrake",config->seperateGasBrake); + WriteOutInt(buffer,bufferPos,"disableAnalogueTCS",config->disableAnalogueTCS); + WriteOutInt(buffer,bufferPos,"reverseGas",config->reverseGas); + + WriteOutString(buffer,bufferPos,"playerName",config->playerName); + WriteOutString(buffer,bufferPos,"gameName",config->gameName); + WriteOutString(buffer,bufferPos,"password",config->password); + WriteOutString(buffer,bufferPos,"confirmedVersion",config->confirmedVersion); + + WriteOutFloat(buffer,bufferPos,"gfxDynamics",config->gfxDynamics); + WriteOutFloat(buffer,bufferPos,"soundVolume",config->soundVolume); + WriteOutFloat(buffer,bufferPos,"musicVolume",config->musicVolume); + WriteOutFloat(buffer,bufferPos,"hudTransparency",config->hudTransparency); + WriteOutFloat(buffer,bufferPos,"ffbIntensity",config->ffbIntensity); + + WriteOutFileName(buffer,bufferPos,"lastCar",config->lastCar); + WriteOutFileName(buffer,bufferPos,"lastEnemy",config->lastEnemy); + WriteOutFileName(buffer,bufferPos,"lastRoad",config->lastRoad); + WriteOutInt(buffer,bufferPos,"numEnemies",config->numEnemies); + WriteOutInt(buffer,bufferPos,"automatic",config->automatic); + WriteOutInt(buffer,bufferPos,"lastLaps",config->lastLaps); + WriteOutInt(buffer,bufferPos,"lastColor",config->lastColor); + WriteOutFileName(buffer,bufferPos,"lastEnv",config->lastEnv); + + WriteOutInt(buffer,bufferPos,"numKeys",config->numKeys); + for(int arrayPos=0;arrayPosnumKeys;arrayPos++) + { + WriteOutArrayPos(buffer,bufferPos,arrayPos); + WriteOutHex(buffer,bufferPos,"keyID",config->keys[arrayPos].keyID); + WriteOutHex(buffer,bufferPos,"controllerID1",config->keys[arrayPos].controllerID1); + WriteOutHex(buffer,bufferPos,"controllerID2",config->keys[arrayPos].controllerID2); + WriteOutHex(buffer,bufferPos,"elementID",config->keys[arrayPos].elementID); + WriteOutString(buffer,bufferPos,"identifier",config->keys[arrayPos].identifier); + WriteOutString(buffer,bufferPos,"controllerIdentifier",config->keys[arrayPos].controllerIdentifier); + } + + WriteOutInt(buffer,bufferPos,"numAxis",config->numAxis); + for(int arrayPos=0;arrayPosnumAxis;arrayPos++) + { + WriteOutArrayPos(buffer,bufferPos,arrayPos); + WriteOutHex(buffer,bufferPos,"axisControllerID1",config->axis[arrayPos].axisControllerID1); + WriteOutHex(buffer,bufferPos,"axisControllerID2",config->axis[arrayPos].axisControllerID2); + WriteOutHex(buffer,bufferPos,"axisElementID",config->axis[arrayPos].axisElementID); + WriteOutString(buffer,bufferPos,"axisIdentifier",config->axis[arrayPos].axisIdentifier); + WriteOutInt(buffer,bufferPos,"min",config->axis[arrayPos].min); + WriteOutInt(buffer,bufferPos,"mid",config->axis[arrayPos].mid); + WriteOutInt(buffer,bufferPos,"max",config->axis[arrayPos].max); + WriteOutFloat(buffer,bufferPos,"deadzone",config->axis[arrayPos].deadzone); + } + WriteOutInt(buffer,bufferPos,"numTaunts",config->numTaunts); + for(int arrayPos=0;arrayPosnumTaunts;arrayPos++) + { + WriteOutArrayPos(buffer,bufferPos,arrayPos); + WriteOutString(buffer,bufferPos,"taunts",config->taunts[arrayPos]); + } + for(int arrayPos=0;arrayPos<11;arrayPos++) + { + WriteOutArrayPos(buffer,bufferPos,arrayPos); + WriteOutFileName(buffer,bufferPos,"opponentCars",config->opponentCars[arrayPos]); + WriteOutInt(buffer,bufferPos,"opponentColors",config->opponentColors[arrayPos]); + } + for(int arrayPos=0;arrayPos<32;arrayPos++) + if(config->challengeRecords[arrayPos]) + { + WriteOutArrayPos(buffer,bufferPos,arrayPos); + WriteOutInt(buffer,bufferPos,"challengeRecords",config->challengeRecords[arrayPos]); + } + + WriteOutInt(buffer,bufferPos,"numPersonalRecords",config->numPersonalRecords); + for(int arrayPos=0;arrayPosnumPersonalRecords;arrayPos++) + { + WriteOutArrayPos(buffer,bufferPos,arrayPos); + WriteOutFileName(buffer,bufferPos,"records.car",config->records[arrayPos].car); + WriteOutFileName(buffer,bufferPos,"records.map",config->records[arrayPos].map); + WriteOutInt(buffer,bufferPos,"records.mode",config->records[arrayPos].mode); + WriteOutInt(buffer,bufferPos,"records.direction",config->records[arrayPos].direction); + WriteOutHex(buffer,bufferPos,"records.time",config->records[arrayPos].time); + } +} + + +int WriteOutFile(tFileRef fileID, void *dataBuffer,int fileType) +{ + char *buffer=NULL; + int bufferPos=0; + switch(fileType) + { + case kParserTypeMapInfoDesc: + WriteOutMapInfo(buffer,&bufferPos,fileID,(tMapInfo*)dataBuffer); + break; + case kParserTypeConfigDesc: + WriteOutConfig(buffer,&bufferPos,fileID,(tSysConfig*)dataBuffer); + break; + } + buffer=(char*)MemoryAllocateBlock(bufferPos); + bufferPos=0; + switch(fileType) + { + case kParserTypeMapInfoDesc: + WriteOutMapInfo(buffer,&bufferPos,fileID,(tMapInfo*)dataBuffer); + break; + case kParserTypeConfigDesc: + WriteOutConfig(buffer,&bufferPos,fileID,(tSysConfig*)dataBuffer); + break; + } + if(fileType==kParserTypeCareerDataDesc) + CryptData(buffer,bufferPos); + FileSetData(fileID,buffer); +} \ No newline at end of file diff --git a/source/writeout.h b/source/writeout.h new file mode 100644 index 0000000..8bd08a0 --- /dev/null +++ b/source/writeout.h @@ -0,0 +1,8 @@ +#ifndef __WRITEOUT +#define __WRITEOUT + +#include "parser.h" + +int WriteOutFile(tFileRef fileID, void *dataBuffer,int fileType); + +#endif \ No newline at end of file