class CarPhys(Phys): def __init__(self, mdt, carphys_props): Phys.__init__(self, mdt) self.pnode = None self.vehicle = None self.curr_speed_factor = 1.0 self.__prev_speed = 0 self.__finds = {} # cache for find's results self.props = carphys_props self._load_phys() self.__set_collision_mesh() self.__set_phys_node() self.__set_vehicle() self.__set_wheels() eng.attach_obs(self.on_end_frame) def _load_phys(self): fpath = self.props.phys_file % self.mdt.name with open(fpath) as phys_file: # pass phys props as a class self.cfg = load(phys_file) self.cfg['max_speed'] = self.get_speed() self.cfg['friction_slip'] = self.get_friction() self.cfg['roll_influence'] = self.get_roll_influence() s_a = (self.mdt.name, round(self.cfg['max_speed'], 2), self.props.driver_engine) LogMgr().log('speed %s: %s (%s)' % s_a) fr_slip = round(self.cfg['friction_slip'], 2) f_a = (self.mdt.name, fr_slip, self.props.driver_tires) LogMgr().log('friction %s: %s (%s)' % f_a) r_a = (self.mdt.name, round(self.cfg['roll_influence'], 2), self.props.driver_suspensions) LogMgr().log('roll %s: %s (%s)' % r_a) s_a = lambda field: setattr(self, field, self.cfg[field]) map(s_a, self.cfg.keys()) def __set_collision_mesh(self): fpath = self.props.coll_path % self.mdt.name self.coll_mesh = loader.loadModel(fpath) chassis_shape = BulletConvexHullShape() for geom in PhysMgr().find_geoms(self.coll_mesh, self.props.coll_name): chassis_shape.addGeom(geom.node().getGeom(0), geom.getTransform()) self.mdt.gfx.nodepath.node().addShape(chassis_shape) self.mdt.gfx.nodepath.setCollideMask( BitMask32.bit(1) | BitMask32.bit(2 + self.props.cars.index(self.mdt.name))) #nodepath = self.mdt.gfx.nodepath.attachNewNode(BulletGhostNode('car ghost')) #nodepath.node().addShape(BulletCapsuleShape(4, 5, ZUp)) #eng.attach_ghost(nodepath.node()) #nodepath.node().notifyCollisions(False) def __set_phys_node(self): self.pnode = self.mdt.gfx.nodepath.node() self.pnode.setMass(self.mass) self.pnode.setDeactivationEnabled(False) PhysMgr().attach_rigid_body(self.pnode) PhysMgr().add_collision_obj(self.pnode) def __set_vehicle(self): self.vehicle = BulletVehicle(PhysMgr().root, self.pnode) self.vehicle.setCoordinateSystem(ZUp) self.vehicle.setPitchControl(self.pitch_control) tuning = self.vehicle.getTuning() tuning.setSuspensionCompression(self.suspension_compression) tuning.setSuspensionDamping(self.suspension_damping) PhysMgr().attach_vehicle(self.vehicle) def __set_wheels(self): fwheel_bounds = self.mdt.gfx.wheels['fr'].get_tight_bounds() f_radius = (fwheel_bounds[1][2] - fwheel_bounds[0][2]) / 2.0 + .01 rwheel_bounds = self.mdt.gfx.wheels['rr'].get_tight_bounds() r_radius = (rwheel_bounds[1][2] - rwheel_bounds[0][2]) / 2.0 + .01 ffr = self.coll_mesh.find('**/' + self.props.wheel_names[0][0]) ffl = self.coll_mesh.find('**/' + self.props.wheel_names[0][1]) rrr = self.coll_mesh.find('**/' + self.props.wheel_names[0][2]) rrl = self.coll_mesh.find('**/' + self.props.wheel_names[0][3]) meth = self.coll_mesh.find fr_node = ffr if ffr else meth('**/' + self.props.wheel_names[1][0]) fl_node = ffl if ffl else meth('**/' + self.props.wheel_names[1][1]) rr_node = rrr if rrr else meth('**/' + self.props.wheel_names[1][2]) rl_node = rrl if rrl else meth('**/' + self.props.wheel_names[1][3]) wheel_fr_pos = fr_node.get_pos() + (0, 0, f_radius) wheel_fl_pos = fl_node.get_pos() + (0, 0, f_radius) wheel_rr_pos = rr_node.get_pos() + (0, 0, r_radius) wheel_rl_pos = rl_node.get_pos() + (0, 0, r_radius) frw = self.mdt.gfx.wheels['fr'] flw = self.mdt.gfx.wheels['fl'] rrw = self.mdt.gfx.wheels['rr'] rlw = self.mdt.gfx.wheels['rl'] wheels_info = [(wheel_fr_pos, True, frw, f_radius), (wheel_fl_pos, True, flw, f_radius), (wheel_rr_pos, False, rrw, r_radius), (wheel_rl_pos, False, rlw, r_radius)] for (pos, front, nodepath, radius) in wheels_info: self.__add_wheel(pos, front, nodepath.node(), radius) def __add_wheel(self, pos, front, node, radius): whl = self.vehicle.createWheel() whl.setNode(node) whl.setChassisConnectionPointCs(LPoint3f(*pos)) whl.setFrontWheel(front) whl.setWheelDirectionCs((0, 0, -1)) whl.setWheelAxleCs((1, 0, 0)) whl.setWheelRadius(radius) whl.setSuspensionStiffness(self.suspension_stiffness) whl.setWheelsDampingRelaxation(self.wheels_damping_relaxation) whl.setWheelsDampingCompression(self.wheels_damping_compression) whl.setFrictionSlip(self.friction_slip) # high -> more adherence whl.setRollInfluence(self.roll_influence) # low -> more stability whl.setMaxSuspensionForce(self.max_suspension_force) whl.setMaxSuspensionTravelCm(self.max_suspension_travel_cm) whl.setSkidInfo(self.skid_info) @property def lateral_force(self): vel = self.vehicle.get_chassis().get_linear_velocity() vel.normalize() dir = self.mdt.logic.car_vec lat = dir.dot(vel) lat_force = 0 if lat > .5: lat_force = min(1, (lat - 1.0) / -.2) return lat_force @property def is_flying(self): # no need to be cached rays = [whl.getRaycastInfo() for whl in self.vehicle.get_wheels()] return not any(ray.isInContact() for ray in rays) @property def prev_speed(self): return self.__prev_speed @property def prev_speed_ratio(self): return max(0, min(1.0, self.prev_speed / self.max_speed)) def on_end_frame(self): self.__prev_speed = self.speed @property def speed(self): if self.mdt.fsm.getCurrentOrNextState() == 'Countdown': return 0 # getCurrentSpeedKmHour returns odd values otherwise return self.vehicle.getCurrentSpeedKmHour() @property def speed_ratio(self): return max(0, min(1.0, self.speed / self.max_speed)) def set_forces(self, eng_frc, brake_frc, steering): self.vehicle.setSteeringValue(steering, 0) self.vehicle.setSteeringValue(steering, 1) self.vehicle.applyEngineForce(eng_frc, 2) self.vehicle.applyEngineForce(eng_frc, 3) self.vehicle.setBrake(brake_frc, 2) self.vehicle.setBrake(brake_frc, 3) def update_car_props(self): speeds = [] for whl in self.vehicle.get_wheels(): contact_pt = whl.get_raycast_info().getContactPointWs() gnd_name = self.gnd_name(contact_pt) if not gnd_name: continue if gnd_name not in self.__finds: gnd = self.props.track_phys.find('**/' + gnd_name) self.__finds[gnd_name] = gnd gfx_node = self.__finds[gnd_name] if gfx_node.has_tag('speed'): speeds += [float(gfx_node.get_tag('speed'))] if gfx_node.has_tag('friction'): fric = float(gfx_node.get_tag('friction')) whl.setFrictionSlip(self.friction_slip * fric) self.curr_speed_factor = (sum(speeds) / len(speeds)) if speeds else 1.0 @property def gnd_names(self): # no need to be cached whls = self.vehicle.get_wheels() pos = map(lambda whl: whl.get_raycast_info().getContactPointWs(), whls) return map(self.gnd_name, pos) @staticmethod def gnd_name(pos): top = pos + (0, 0, 20) bottom = pos + (0, 0, -20) result = PhysMgr().ray_test_closest(bottom, top) ground = result.get_node() return ground.get_name() if ground else '' def apply_damage(self, reset=False): if reset: self.max_speed = self.get_speed() self.friction_slip = self.get_friction() self.roll_influence = self.get_roll_influence() else: self.max_speed *= .95 self.friction_slip *= .95 self.roll_influence *= 1.05 fric = lambda whl: whl.setFrictionSlip(self.friction_slip) map(fric, self.vehicle.get_wheels()) roll = lambda whl: whl.setRollInfluence(self.roll_influence) map(roll, self.vehicle.get_wheels()) s_a = (str(round(self.max_speed, 2)), self.props.driver_engine) LogMgr().log('speed: %s (%s)' % s_a) f_a = (str(round(self.friction_slip, 2)), self.props.driver_tires) LogMgr().log('friction: %s (%s)' % f_a) r_a = (str(round(self.roll_influence, 2)), self.props.driver_suspensions) LogMgr().log('roll: %s (%s)' % r_a) def get_speed(self): return self.cfg['max_speed'] * (1 + .01 * self.props.driver_engine) def get_friction(self): return self.cfg['friction_slip'] * (1 + .01 * self.props.driver_tires) def get_roll_influence(self): return self.cfg['roll_influence'] * ( 1 + .01 * self.props.driver_suspensions) def destroy(self): eng.detach_obs(self.on_end_frame) eng.remove_vehicle(self.vehicle) self.pnode = self.vehicle = self.__finds = self.__track_phys = \ self.coll_mesh = None Phys.destroy(self)
class CarPhys(PhysColleague): def __init__(self, mediator, car_props, tuning, players): PhysColleague.__init__(self, mediator) self.pnode = self.vehicle = self.__track_phys = self.coll_mesh = \ self.max_speed = self.friction_slip = \ self.friction_slip_rear = self.cfg = None self.turbo = False self._tuning = tuning self._players = players self.roll_influence = [] self.ai_meshes = [] self.curr_speed_mul = 1.0 self.roll_influence_k = self.friction_slip_k = 1.0 self.__prev_speed = 0 self.__last_drift_time = 0 self.__finds = {} # cache for find's results self.__whl2flytime = {} self.cprops = car_props self._load_phys() self.__set_collision_mesh() self.__set_ai_meshes() self.__set_phys_node() self.__set_vehicle() self.__set_wheels() self.eng.attach_obs(self.on_end_frame) def _load_phys(self): ppath = self.cprops.race_props.season_props.gameprops.phys_path fpath = ppath % self.cprops.name with open(fpath) as phys_file: self.cfg = load(phys_file) # they may be changed by drivers and tuning self.cfg['max_speed'] = self.get_speed() self.cfg['friction_slip'] = self.get_friction_static()[0] self.cfg['friction_slip_rear'] = self.get_friction_static()[1] self.cfg['roll_influence'] = self.get_roll_influence_static() self.friction_slip = self.cfg['friction_slip'] self.friction_slip_rear = self.cfg['friction_slip_rear'] self.__log_props() set_a = lambda field: setattr(self, field, self.cfg[field]) list(map(set_a, self.cfg.keys())) def __log_props(self, starting=True): s_s = self.cfg['max_speed'] if starting else self.max_speed s_f = self.cfg['friction_slip'] if starting else \ self.get_friction_static()[0] s_fr = self.cfg['friction_slip_rear'] if starting else \ self.get_friction_static()[1] s_r = self.cfg['roll_influence'] if starting else \ self.get_roll_influence_static() log_info = [('speed', self.cprops.name, round(s_s, 2), self.cprops.driver_engine), ('friction 0', self.cprops.name, round(s_f[0], 2), self.cprops.driver_tires), ('friction 1', self.cprops.name, round(s_f[1], 2), self.cprops.driver_tires), ('friction_rear 0', self.cprops.name, round(s_fr[0], 2), self.cprops.driver_tires), ('friction_rear 1', self.cprops.name, round(s_fr[1], 2), self.cprops.driver_tires), ('roll min', self.cprops.name, round(s_r[0], 2), self.cprops.driver_suspensions), ('roll max', self.cprops.name, round(s_r[1], 2), self.cprops.driver_suspensions)] for l_i in log_info: info('%s %s: %s (%s)' % l_i) def __set_collision_mesh(self): fpath = self.cprops.race_props.coll_path % self.cprops.name try: self.coll_mesh = self.eng.load_model(fpath) except OSError: # new cars don't have collision meshes self.coll_mesh = self.eng.load_model( fpath.replace('capsule', 'car')) # chassis_shape = BulletConvexHullShape() # for geom in self.eng.lib.find_geoms( # self.coll_mesh, self.cprops.race_props.coll_name): # chassis_shape.add_geom(geom.node().get_geom(0), # geom.get_transform()) # self.mediator.gfx.nodepath.get_node().add_shape(chassis_shape) chassis_shape = BulletBoxShape(tuple(self.cfg['box_size'])) boxpos = self.cfg['box_pos'] boxpos[2] += self.cfg['center_mass_offset'] pos = TransformState.makePos(tuple(boxpos)) self.mediator.gfx.nodepath.p3dnode.add_shape(chassis_shape, pos) car_names = [player.car for player in self._players] car_idx = car_names.index(self.cprops.name) car_bit = BitMask32.bit(BitMasks.car(car_idx)) ghost_bit = BitMask32.bit(BitMasks.ghost) track_bit = BitMask32.bit(BitMasks.track_merged) mask = car_bit | ghost_bit | track_bit self.mediator.gfx.nodepath.set_collide_mask(mask) def __set_ai_meshes(self): return # if we attach these meshes (or even only one mesh, box, sphere, # whatever) then the collision between the goal and the vehicle doesn't # work properly h = .5 boxsz = self.cfg['box_size'] hs = [] hs += [ h / 2 + boxsz[2] * 2 + self.cfg['box_pos'][2] + self.cfg['center_mass_offset'] ] hs += [ -h / 2 - boxsz[2] * 2 + self.cfg['box_pos'][2] + self.cfg['center_mass_offset'] ] for _h in hs: shape = BulletBoxShape((boxsz[0], boxsz[1], h)) ghost = GhostNode('Vehicle') pos = TransformState.makePos((0, 0, _h)) ghost.node.addShape(shape, pos) self.ai_meshes += [self.eng.attach_node(ghost.node)] car_names = self.cprops.race_props.season_props.car_names car_idx = car_names.index(self.cprops.name) car_bit = BitMask32.bit(BitMasks.car(car_idx)) ghost_bit = BitMask32.bit(BitMasks.ghost) mask = car_bit | ghost_bit self.ai_meshes[-1].set_collide_mask(mask) self.eng.phys_mgr.attach_ghost(ghost.node) def __set_phys_node(self): self.pnode = self.mediator.gfx.nodepath.p3dnode self.pnode.set_mass(self.mass) # default 0 self.pnode.set_deactivation_enabled(False) self.eng.phys_mgr.attach_rigid_body(self.pnode) self.eng.phys_mgr.add_collision_obj(self.pnode) def __set_vehicle(self): self.vehicle = BulletVehicle(self.eng.phys_mgr.root._wld, self.pnode) # access to a protected member self.vehicle.set_coordinate_system(ZUp) self.vehicle.set_pitch_control(self.pitch_control) tuning = self.vehicle.get_tuning() tuning.set_suspension_compression(self.suspension_compression) # default .83 tuning.set_suspension_damping(self.suspension_damping) # default .88 self.eng.phys_mgr.attach_vehicle(self.vehicle) def __set_wheels(self): wheels = self.mediator.gfx.wheels f_bounds = wheels['fr'].tight_bounds f_radius = (f_bounds[1][2] - f_bounds[0][2]) / 2.0 + .01 r_bounds = wheels['rr'].tight_bounds r_radius = (r_bounds[1][2] - r_bounds[0][2]) / 2.0 + .01 wheel_names = self.cprops.race_props.wheel_names ffr = self.coll_mesh.find('**/' + wheel_names.frontrear.fr) ffl = self.coll_mesh.find('**/' + wheel_names.frontrear.fl) rrr = self.coll_mesh.find('**/' + wheel_names.frontrear.rr) rrl = self.coll_mesh.find('**/' + wheel_names.frontrear.rl) meth = self.coll_mesh.find fr_node = ffr if ffr else meth('**/' + wheel_names.both.fr) fl_node = ffl if ffl else meth('**/' + wheel_names.both.fl) rr_node = rrr if rrr else meth('**/' + wheel_names.both.rr) rl_node = rrl if rrl else meth('**/' + wheel_names.both.rl) if not fr_node: # new cars fr_node = meth('**/w_fr') fl_node = meth('**/w_fl') rr_node = meth('**/w_rr') rl_node = meth('**/w_rl') offset = self.cfg['center_mass_offset'] fr_pos = fr_node.get_pos() + (0, 0, f_radius + offset) fl_pos = fl_node.get_pos() + (0, 0, f_radius + offset) rr_pos = rr_node.get_pos() + (0, 0, r_radius + offset) rl_pos = rl_node.get_pos() + (0, 0, r_radius + offset) wheels_info = [(fr_pos, True, wheels['fr'], f_radius), (fl_pos, True, wheels['fl'], f_radius), (rr_pos, False, wheels['rr'], r_radius), (rl_pos, False, wheels['rl'], r_radius)] for i, (pos, front, nodepath, radius) in enumerate(wheels_info): self.__add_wheel(pos, front, nodepath.p3dnode, radius, i) def __add_wheel(self, pos, is_front, node, radius, i): whl = self.vehicle.create_wheel() whl.set_node(node) whl.set_chassis_connection_point_cs(LPoint3f(*pos)) whl.set_front_wheel(is_front) whl.set_wheel_direction_cs((0, 0, -1)) whl.set_wheel_axle_cs((1, 0, 0)) whl.set_wheel_radius(radius) whl.set_suspension_stiffness(self.suspension_stiffness[0]) # default 5.88 whl.set_wheels_damping_relaxation(self.wheels_damping_relaxation[0]) # default .88 whl.set_wheels_damping_compression(self.wheels_damping_compression[0]) # default .83 idx = 0 if is_front else 1 whl.set_friction_slip(self.get_friction_static()[idx][0]) # default 10.5 # friction slip high -> more adherence whl.set_roll_influence(self.roll_influence[0]) # low -> more stability # default .1 whl.set_max_suspension_force(self.max_suspension_force) # default 6000 whl.set_max_suspension_travel_cm(self.max_suspension_travel_cm) # default 500 whl.set_skid_info(self.skid_info) # default 0 self.__whl2flytime[i] = 0 @property def lateral_force(self): vel = self.vehicle.get_chassis().get_linear_velocity() rot_mat = Mat4() rot_mat.setRotateMat(-90, (0, 0, 1)) car_lat = rot_mat.xformVec(self.mediator.logic.car_vec._vec) # access to a protected member proj_frc = vel.project(car_lat) return proj_frc.length() @property def lin_vel(self): return self.vehicle.get_chassis().get_linear_velocity().length() * 3.6 @property def is_flying(self): # no need to be cached rays = [whl.get_raycast_info() for whl in self.vehicle.get_wheels()] return not any(ray.is_in_contact() for ray in rays) @property def is_drifting(self): return self.lateral_force > 4.0 @property def last_drift_time(self): return self.__last_drift_time @property def prev_speed(self): return self.__prev_speed @property def prev_speed_ratio(self): return max(0, min(1.0, self.prev_speed / self.max_speed)) def on_end_frame(self): self.__prev_speed = self.speed @property def speed(self): if self.mediator.fsm.getCurrentOrNextState() == 'Countdown': return 0 # getCurrentSpeedKmHour returns odd values otherwise return self.vehicle.get_current_speed_km_hour() @property def speed_ratio(self): return max(0, min(1.0, self.speed / self.max_speed)) @property def lin_vel_ratio(self): return max(0, min(1.0, self.lin_vel / self.max_speed)) def set_forces(self, eng_frc, brake_frc, brk_ratio, steering): idx = 1 if self.mediator.logic.is_drifting else 0 eng_frc_ratio = self.engine_acc_frc_ratio[idx] self.vehicle.set_steering_value(steering, 0) self.vehicle.set_steering_value(steering, 1) self.vehicle.apply_engine_force(eng_frc * eng_frc_ratio, 0) self.vehicle.apply_engine_force(eng_frc * eng_frc_ratio, 1) self.vehicle.apply_engine_force(eng_frc * (1 - eng_frc_ratio), 2) self.vehicle.apply_engine_force(eng_frc * (1 - eng_frc_ratio), 3) self.vehicle.set_brake((1 - brk_ratio) * brake_frc, 2) self.vehicle.set_brake((1 - brk_ratio) * brake_frc, 3) self.vehicle.set_brake(brk_ratio * brake_frc, 0) self.vehicle.set_brake(brk_ratio * brake_frc, 1) def update_car_props(self): wheels = zip(self.vehicle.get_wheels(), range(4)) speeds = list(map(lambda whi: self.__update_whl_props(*whi), wheels)) speeds = [speed for speed in speeds if speed] if self.is_drifting: self.__last_drift_time = self.eng.curr_time self.curr_speed_mul = (sum(speeds) / len(speeds)) if speeds else 1.0 def __update_whl_props(self, whl, i): susp_min = self.suspension_stiffness[0] susp_max = self.suspension_stiffness[1] susp_diff = susp_max - susp_min whl.set_suspension_stiffness(susp_min + self.speed_ratio * susp_diff) relax_min = self.wheels_damping_relaxation[0] relax_max = self.wheels_damping_relaxation[1] relax_diff = relax_max - relax_min relax = relax_min + self.speed_ratio * relax_diff whl.set_wheels_damping_relaxation(relax) compr_min = self.wheels_damping_compression[0] compr_max = self.wheels_damping_compression[1] compr_diff = compr_max - compr_min compr = compr_min + self.speed_ratio * compr_diff whl.set_wheels_damping_compression(compr) roll_infl_min = self.roll_influence[0] roll_infl_max = self.roll_influence[1] roll_infl_diff = roll_infl_max - roll_infl_min roll_infl = roll_infl_min + self.speed_ratio * roll_infl_diff whl.set_roll_influence(self.roll_influence_k * roll_infl) contact_pt = whl.get_raycast_info().getContactPointWs() gnd_name = self.gnd_name(contact_pt) if not gnd_name or gnd_name in ['Vehicle', 'Wall', 'Respawn']: return if gnd_name not in self.__finds: gnd = self.cprops.race.track.phys.model.find('**/' + gnd_name) self.__finds[gnd_name] = gnd gfx_node = self.__finds[gnd_name] if not gfx_node: print('ground error', gnd_name) return fric = 1.0 if gfx_node.has_tag('friction'): fric = float(gfx_node.get_tag('friction')) if not whl.get_raycast_info().is_in_contact(): self.__whl2flytime[i] = self.eng.curr_time gnd_time = self.eng.curr_time - self.__whl2flytime[i] gnd_recovery_time = .2 if whl.is_front_wheel() else .1 gnd_factor = min(1, gnd_time / gnd_recovery_time) idx = 0 if whl.is_front_wheel() else 1 turbo_factor = 1.24 if self.turbo else 1.0 whl.setFrictionSlip(self.get_friction()[idx] * fric * gnd_factor * turbo_factor) if gfx_node.has_tag('speed'): return float(gfx_node.get_tag('speed')) @property def gnd_names(self): # no need to be cached whls = self.vehicle.get_wheels() pos = list( map(lambda whl: whl.get_raycast_info().get_contact_point_ws(), whls)) return list(map(self.gnd_name, pos)) @staticmethod def gnd_name(pos): top, bottom = pos + (0, 0, 20), pos + (0, 0, -20) result = CarPhys.eng.phys_mgr.ray_test_closest(bottom, top) ground = result.get_node() return ground.get_name() if ground else '' @staticmethod def gnd_height(pos): # this should be a method of the track top, bottom = pos + (0, 0, 20), pos + (0, 0, -20) result = CarPhys.eng.phys_mgr.ray_test_closest(bottom, top) hit_pos = result.get_hit_pos() return hit_pos.z if hit_pos else None def apply_damage(self, reset=False): # wheels = self.vehicle.get_wheels() if reset: self.max_speed = self.get_speed() self.friction_slip_k = 1.0 self.roll_influence_k = 1.0 else: self.max_speed *= .95 self.friction_slip_k *= .95 self.roll_influence_k *= 1.05 # map(lambda whl: whl.set_friction_slip(self.friction_slip), wheels) self.__log_props(False) def get_speed(self): return self.cfg['max_speed'] * (1 + .01 * self.cprops.driver_engine) def get_friction(self): k = (1 + .01 * self.cprops.driver_tires) return self.friction_slip[0] * k, self.friction_slip_rear[0] * k def get_roll_influence_static(self): min_r = self.cfg['roll_influence'][0] max_r = self.cfg['roll_influence'][1] k = 1 + .01 * self.cprops.driver_suspensions return [min_r * k, max_r * k] def get_friction_static(self): k = 1 + .01 * self.cprops.driver_tires fstr = 'friction_slip' return [(self.cfg[fstr][0] * k, self.cfg[fstr][1] * k), (self.cfg[fstr + '_rear'][0] * k, self.cfg[fstr + '_rear'][1] * k)] def get_roll_influence(self): min_r = self.cfg['roll_influence'][0] max_r = self.cfg['roll_influence'][1] diff_r = max_r - min_r curr_r = min_r + self.speed_ratio * diff_r return curr_r * (1 + .01 * self.cprops.driver_suspensions) def rotate(self): self.pnode.apply_torque((0, 0, 80000)) self.mediator.logic.applied_torque = True def destroy(self): self.eng.detach_obs(self.on_end_frame) self.eng.phys_mgr.remove_vehicle(self.vehicle) self.pnode = self.vehicle = self.__finds = self.__track_phys = \ self.coll_mesh = None PhysColleague.destroy(self)