diff --git a/source/default_mp.cfg b/source/default_mp.cfg index ab451ca..406bd2c 100644 --- a/source/default_mp.cfg +++ b/source/default_mp.cfg @@ -7,7 +7,7 @@ sets _match_team2 "-" sets _match_score "-" sets _match_round "-" -sets _zpam "3.32" // ZPAM_RENAME +sets _zpam "3.33-LAN" // ZPAM_RENAME diff --git a/source/images/a_training_target.iwi b/source/images/a_training_target.iwi new file mode 100644 index 0000000..2d35897 Binary files /dev/null and b/source/images/a_training_target.iwi differ diff --git a/source/maps/mp/gametypes/_aim_trainer.gsc b/source/maps/mp/gametypes/_aim_trainer.gsc new file mode 100644 index 0000000..b7b8120 --- /dev/null +++ b/source/maps/mp/gametypes/_aim_trainer.gsc @@ -0,0 +1,765 @@ +#include maps\mp\gametypes\global\_global; + +/* +Map may contain targets that players can use to practise their aim +Names of entities: + Targets (array of targets): + - "trainer_target" - image of target + - "trainer_trigger" - trigger_damage + + Activators (2 per map - for allies and axis): + - "trainer_ground" - image of target activator + - "trainer_ground_trigger" - trigger_damage +*/ + +Init() +{ + addEventListener("onConnected", ::onConnected); + addEventListener("onDisconnect", ::onDisconnect); + addEventListener("onSpawned", ::onSpawned); + + level.aimTargets = []; + + // Load targets and activators + targets = getentarray("trainer_target", "targetname"); + targets_damage = getentarray("trainer_trigger", "targetname"); + activators = getent("trainer_activator", "targetname"); + activators_damage = getentarray("trainer_activator_damage", "targetname"); + activators_hint = getentarray("trainer_activator_hint", "targetname"); +/* + println("## trainer_target = " + targets.size); + println("## trainer_trigger = " + targets_damage.size); + println("## trainer_activator = " + isDefined(activators)); + println("## trainer_activator_damage = " + activators_damage.size); + println("## trainer_activator_hint = " + activators_hint.size); +*/ + if (targets.size == 0 || !isDefined(activators)) + return; + if (targets.size != targets_damage.size) + return; + + + // Not allowed -> delete + if (!level.in_readyup) + { + for (i = 0; i < targets.size; i++) + { + targets[i] delete(); + targets_damage[i] delete(); + } + for (i = 0; i < activators.size; i++) + { + activators_damage[i] delete(); + activators_hint[i] delete(); + } + activators delete(); + return; + } + + + + level._effect["aimTargetExplosion"] = loadfx("fx/impacts/large_metalhit.efx"); + + + + for (i = 0; i < targets.size; i++) + { + targets_damage[i] enablelinkto(); + targets_damage[i] linkTo(targets[i]); + targets_damage[i].parent = targets[i]; + + targets[i] notsolid(); + targets[i].trigger_damage = targets_damage[i]; + targets[i].origin_original = targets[i].origin; + //targets[i].trigger_damage thread debugDamage("["+i+"]"); + //targets[i] thread maps\mp\gametypes\global\developer::showWaypoint(); + } + + + for (i = 0; i < activators_damage.size; i++) + { + //activators[i] notsolid(); + activators_damage[i] thread handleActivation(); + } + + + level.aimTargets = targets; + + println("## level.aimTargets = " + level.aimTargets.size); // TODO +} + +onConnected() +{ + self.aimTrainerMode = 0; + self.aimTrainer_outOfAmmo = false; + self.aimTrainer_startAngles = (0, 0, 0); +} + + +onDisconnect() +{ + self turnOff(); +} + +onSpawned() +{ + self turnOff(); +} + + + +reserveTargetBy(player) +{ + for (i = 0; i < level.aimTargets.size; i++) + { + target = level.aimTargets[i]; + + if (!isDefined(target.reservedBy)) + { + target.reservedBy = player; + + //player iprintln("reserving index " + i); + + return target; + } + } + return undefined; +} + +releaseTarget() +{ + target = self; + + target hide(); + target.reservedBy = undefined; + target.origin = target.origin_original; + + target notify("aim_trainer_target_released"); +} + +rotateTowards(player) +{ + target = self; + + target endon("aim_trainer_target_released"); + + for (;;) + { + if (!isDefined(target) || !isDefined(player)) + return; + + angles = vectortoangles(player.origin - target.origin); + + target.angles = (0, angles[1] + 180, 0); + + wait level.frame; + waittillframeend; + } +} + + + +debugDamage(name) +{ + for(;;) + { + self waittill("damage", dmg, player); + + iprintln("Damage " + dmg + " to " + name + " by " + player.name); + } +} + +handleDamageForPlayer(player) +{ + target = self; + + target endon("aim_trainer_target_released"); + + for(;;) + { + target.trigger_damage waittill("damage", dmg, attacker); + + // Ignore damages for other players + if (attacker != player) + continue; + + player thread maps\mp\gametypes\_damagefeedback::updateDamageFeedback(undefined, 1); + + player thread generateMeNewTargetPosition("kill"); + } +} + +handleActivation() +{ + activator_damage = self; + + for(;;) + { + activator_damage waittill("damage", dmg, player); + + player thread maps\mp\gametypes\_damagefeedback::updateDamageFeedback(undefined, 1); + + //player toggle(); + + player iprintlnbold("Aim trainer"); + player iprintlnbold("Hold ^3[{+melee_breath}] ^7to enable / switch modes."); + player iprintlnbold("Double press ^3[{+melee_breath}] ^7to disable."); + player iprintlnbold(" "); + player iprintlnbold(" "); + } +} + + +setKillTimeout(seconds, player) +{ + target = self; + + target endon("aim_trainer_target_released"); + target notify("aim_trainer_kill_timeout"); + target endon("aim_trainer_kill_timeout"); + + wait level.fps_multiplier * seconds; + + player iprintln("^1Time to kill elapsed!"); + + player thread generateMeNewTargetPosition("missed"); +} + + + + + +getTargetPos(randomize) +{ + eye = self maps\mp\gametypes\global\player::getEyeOrigin(); + angles = self getPlayerAngles(); + fov = self getFOV() / 80; + + + + // Make sure the target is always visible on screen + angles_new = angles; + if (randomize) + { + angleDiff = angles[1] - self.aimTrainer_startAngles[1]; + if (angleDiff > 180) angleDiff -= 360; + else if (angleDiff < -180) angleDiff += 360; + + // Player is not looking to startAngles direction, save new + if (angleDiff < -45 || angleDiff > 45) + self.aimTrainer_startAngles = angles; + + // left +, right -, max 40 + left_right = randomint(40) - 20; // -20 <-> 19 + left_right *= fov; + left_right += self.aimTrainer_startAngles[1]; + + // up -, down + + up_down = randomint(14) - 8; // -8 <-> 5 + up_down *= fov; + + // In center put target always above weapon target + if (up_down > angles[0] && self playerAds() > 0 && !maps\mp\gametypes\_weapons::isSniper(self getcurrentweapon())) + { + left_right_diff = (angles[1] - left_right); + if (left_right_diff > -10 && left_right_diff <= 0) + { + left_right += 10; + //iprintln("adjusted target more to the left"); + + } + else if (left_right_diff < 10 && left_right_diff > 0) + { + left_right -= 10; + //iprintln("adjusted target more to the right"); + } + } + + + angles_new = (up_down, left_right, 0); + } + + distance = 200 + (800 - (800 * fov)); + + forward = anglesToForward(angles_new); + forwardMultiplier = distance; + + forwardOrigin = eye + (forward[0] * forwardMultiplier, forward[1] * forwardMultiplier, forward[2] * forwardMultiplier); + + + forwardTrace = Bullettrace(eye, forwardOrigin, false, self); // BulletTrace( , , , ) + forwardTracedOrigin = forwardTrace["position"]; + + + vector = (forwardTracedOrigin - eye); + + vectorNorm = VectorNormalize(vector); + vectorLen = length(vector); + + multiplier = vectorLen * 0.8; + + newPos = eye + (vectorNorm[0] * multiplier, vectorNorm[1] * multiplier, vectorNorm[2] * multiplier); + + return newPos; +} + +moving(speed) +{ + target = self; + + target endon("aim_trainer_target_released"); + target endon("aim_trainer_target_updated"); + + // Wait till properly rotated and spawned + waittillframeend; + + // Generate moving direction + right = anglesToRight(target.angles); + distance = 100; // to both sides + + origin_original = target.origin; + origin_right = target.origin + (right[0] * distance, right[1] * distance, right[2] * distance); + origin_left = target.origin + (right[0] * distance * -1, right[1] * distance * -1, right[2] * distance * -1); + + rightTrace = Bullettrace(origin_original, origin_right, false, target); // BulletTrace( , , , ) + leftTrace = Bullettrace(origin_original, origin_left, false, target); // BulletTrace( , , , ) + + new_distance_right = distance(origin_original, rightTrace["position"]) * 0.8; + new_distance_left = distance(origin_original, leftTrace["position"]) * 0.8; + + origin[0] = origin_original + (right[0] * new_distance_right, right[1] * new_distance_right, right[2] * new_distance_right); + origin[1] = origin_original + (right[0] * new_distance_left * -1, right[1] * new_distance_left * -1, right[2] * new_distance_left * -1); + + total_distance = distance(origin[0], origin[1]); + + speed = speed * (total_distance / (distance*2)); + time[0] = speed * (new_distance_right / (distance*2)); + time[1] = speed * (new_distance_left / (distance*2)); + + + + + + + index_1 = 0; + index_2 = 1; + rand = randomint(2); // 0 - 1 + if (rand) + { + index_1 = 1; + index_2 = 0; + } + +/* + if (!isDefined(target.waypoint1)) + { + target.waypoint1 = spawnstruct(); + target.waypoint1.origin = origin[index_1]; + target.waypoint1 thread maps\mp\gametypes\global\developer::showWaypoint(undefined, 1); + } + if (!isDefined(target.waypoint2)) + { + target.waypoint2 = spawnstruct(); + target.waypoint2.origin = origin[index_2]; + target.waypoint2 thread maps\mp\gametypes\global\developer::showWaypoint(undefined, 1); + } + target.waypoint1.origin = origin[index_1]; + target.waypoint2.origin = origin[index_2]; +*/ +/* + iprintln("speed: ("+speed+")"); + iprintln("right: ("+time[0]+")"); + iprintln("left: ("+time[1]+")"); +*/ + firstMove = true; + for(;;) + { + accel = 0.1; + time1 = speed; + time2 = speed; + accel1 = time1*accel; + if (firstMove) + { + time1 = time[index_1]; // first move is faster with no accell + accel1 = 0; + firstMove = false; + } + + target moveto(origin[index_1], time1, accel1, time1*accel); // moveto( ,