zPAM 3.30 changes (click to open)
@@ -237,8 +276,8 @@ r_polygonOffsetScale and r_polygonOffsetBias warning appears even if they were c
## Installation
-- Download zPAM 3.31 PREVIEW and extract files into following locations:
- - ./Call of Duty 2/main/zpam331.iwd
+- Download zPAM 3.32 PREVIEW and extract files into following locations:
+ - ./Call of Duty 2/main/zpam332.iwd
- ./Call of Duty 2/main/zpam_maps_v2.iwd (*required only for 1.3 game version)
- ./Call of Duty 2/main/server.cfg
@@ -255,12 +294,13 @@ r_polygonOffsetScale and r_polygonOffsetBias warning appears even if they were c
❗ Error "PAM is not installed correctly" may show. ❗
To fix this error, follow instructions in [Troubleshooting](#troubleshooting) section
+Make sure the server is runned without a mod (```/fs_game```).
+PAM uses default main folder because mods does not exec player's configs correctly and settings changed in game would not be persisted to next game session.
+
+If you are running server manually, this is example of command line parameters:
+```+set dedicated 2 +set sv_punkbuster 0 +pb_sv_disable +exec server.cfg```
-
- Gameservers.com settings example (click to open)
-
-
@@ -292,7 +332,7 @@ Big thanks for HQ, HTF and RE gametypes integration and overal PAM testing
Big thanks for PAM promoting
**Other supporters:**
-cokY, Sk1lzZ, YctN, kebit, foxbuster, <==Mustang==>Clan from Hungary, hubertgruber / dutch, excel, shady
+cokY, Sk1lzZ, YctN, kebit, foxbuster, <==Mustang==>Clan from Hungary, hubertgruber / dutch, excel, shady, jza
@@ -411,15 +451,21 @@ You can debug this in game via command **/rcon debug_torsohitbox 1**
There are some weird situations when you shot player to the body, but the game process it as a hit only.
These types of bugs are probably caused by badly implemented hit boxes within the game engine.
In these situations the game process the hit location as hand / arm instead of body (when the arm is right behind the body)
-Hand hitbox fix tries to address this issue by these rules:
- - if you fire from rifle or scope to left or right arm, and
- - enemy is in ads (zoomed), and
- - distance is more than 200, and
- - left or right hind is in front of body (correct angles check)
- -
- - In this case, hit location is changed to body, causing deadly damage
- - You can debug hitbox fix in game via command **/rcon debug_handhitbox 1**
- - **Hand hitbox fix is enabled by default**
+Hand hitbox fix tries to address this issue by the following check:
+
+ - if you fire from rifle or scope, and
+ - distance between you and enemy is more than 200, and
+ - hit location is inside a box created around head, and
+ - head is visible to player (is not behind wall), and
+ - damage is less then 100 (meaning it was hit only to hand)
+ - In that case, damage is changed to 100 (meaning enemy will be killed)
+
+The box is created in a way that its always aligned with your point of view and with deph wider towars you. The box is created in this way to cover hands of enemy inside the box when player is in ADS (zoomed). In that case, if you hit the hand from your point of view, it should be a kill, because body is behind the hand.
+
+To apply this fix, the hit location must be inside the box. If the hitbox is outside the box, original damage values are applied.
+
+You can debug hitbox fix in game via command **/rcon debug_handhitbox 1**
+**Hand hitbox fix is enabled by default**
@@ -436,23 +482,24 @@ At the start of the round and at the end of the round, info about weapons of pla
### How does the "Climbing fix" works / what is it
-If you climb ladder or wall, the "weapon switch" sound was made only for 1st person player, it was silent for other players. Now the sound matches equally, so you hear exactly what other players hear (meaning if you climb ladder or wall, "weapon switch" sound is made for everybody).
-If you want to silently climb ladder or wall, you need to hold weapon down by scrolling down 2x and hold left mouse
+If you climb ladder or wall, the "weapon switch" sound was made only for 1st person player, it was silent for other players. The sound is now removed completly - meaning if you climb ladder or wall, "weapon switch" sound is not played at all - same for everybody
###### Original:
-| Player | Climb ladder
(weapon normal) | Climb wall
(weapon normal) | Climb ladder
(weapon holded down) | Climb wall
(weapon holded down) |
-|---------------|---------------|---------------|-----------------|-------------|
-| You | sound | sound | - | - |
-| Others | - | - | - | - |
+| Player | Climb ladder | Climb wall |
+|---------------|---------------|---------------|
+| You | sound | sound |
+| Others | - | - |
-###### zPAM3.31:
+###### since zPAM3.32:
-| Player | Climb ladder
(weapon normal) | Climb wall
(weapon normal) | Climb ladder
(weapon holded down) | Climb wall
(weapon holded down) |
-|---------------|---------------|---------------|-----------------|-------------|
-| You | sound | sound | - | - |
-| Others | sound | sound | - | - |
+| Player | Climb ladder | Climb wall |
+|---------------|---------------|---------------|
+| You | - | - |
+| Others | - | - |
+
+This sound is played only if you regularly and intentionally switch the weapon
@@ -506,7 +553,7 @@ If you change a color, its applied to team, so your teammates can also see the c
### How does the "Round report" works
-At the end of the round, hit + kill informations are printed. It include damage value, hit location and first hit location. For shotgun it prints number of pellets that hit the target
+At the end of the round, report of hits and kills is printed. It include damage value, hit location and first hit location. For shotgun it prints number of pellets that hit the target and range. Read star indicates that "Hand hitbox fix" or "Torso hitbox fix" was applied.
@@ -573,16 +620,26 @@ So diagonal is not affected by PAM.
-### Scoreboard menu - how the Score, Kills, Deaths, Assists, Hits, Plant and Defuses are counted
+
+### Scoreboard menu
+
+
+Features:
+- shows statistics of players - Score, Kills, Deaths, Assists, Hits, Grenade kills, Plants and Defuses
+- It will always show disconnected players (with [-] prefix) for 5 mins (so you see stats for all players in final scorebard)
+- Only Kills and Deaths will are showed for enemy team (to avoid recognize enemy position by watching the scoreboard)
+
+##### How the Score, Kills, Deaths, Assists, Hits, Plant and Defuses are counted
| Column | Description |
|---------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| Score | Sum of following items:
Kill +1
Teamkill -1
Assist +0.5
Plant +0.5
Defuse +0.5 |
+| Score | Sum of following items:
- Kill: +1
- Teamkill: -1
- Assist: +0.5
- Plant: +0.5
- Defuse: +0.5 |
| Kills | Number of killed enemy players.
Teamkills are not counted (neither substracted) |
| Deaths | Number of times you were killed |
| Assists | Number of players you damaged and were killed by your teammate within 5 second |
-| Hits | Damage you inflicted to opponent that does not lead to kill.
For example, if you damage a player for 75hp, after the player's health is regenerated, you will recieve +0.75 hit points. |
+| Hits | Points given when you hit an enemy and the enemy is then fully healed
- Scopes + rifles (single shot weapons): +1
- Semi/Automatic/Shotgun (other weapons): +0.5
- Pistols: 0
The value is counted only once after the hited enemy is fully healed
Value is colored yellow if its value is >=5.0 and red if its value is >=10.0 |
+| Grenades | Number of kills with grenade |
| Plants | Number of bomb plants |
| Defuses | Number of bomd defuses |
@@ -612,7 +669,7 @@ Example: "autorecording_1|matchinfo_1|score_0|playersleft_1"
## Troubleshooting
### Error "zPAM is not installed correctly"
-#### Iwd file zpam331.iwd must be installed in main folder. (fs_game)
+#### Iwd file zpam332.iwd must be installed in main folder. (fs_game)
- From version 3.20, all iwd files have to be installed in main folder.
This is because of bug that cvars / settings changed in game are not saved into the config when running a game with fs_game set.
Make sure cvar /fs_game is empty and iwd files are placed in main folder.
@@ -720,7 +777,7 @@ Attackers should take both objectives to the goal (blue box) to win. (or elimina
|--------|---|
| mr3, mr10, mr12, mr15 | Max rounds mrxx+1 (SD, RE only) |
| 20rounds | first to 21 wins (SD, RE only) |
-| 15min, 30min, 60min, unlim | total time limit (TDM, DM, HQ, CTF, HTF only) |
+| 10min, 15min, 30min, 60min, unlim | total time limit (TDM, DM, HQ, CTF, HTF only) |
##### Options
@@ -929,7 +986,7 @@ Added posibility to call bash mode from menu.
[3.3.44] Cvar system rewrited; any change to server settings is retained even when map changes; new cvar /pam_mode_custom is defined - it tells that changes made to server settings stays between map
[3.3.45] Warnings about wrong server settings (no password, cheats enabled, punkbuster disabled, cvars changes) is changed; its showed in left top corner under the score; if some of the server settings is changed, detailed list of changed cvars is showed; punkbuster warning is removed
-[3.3.46] Hand hit box fix - if hands are in front of body and game somehow badly interprets it as hit to the hand, PAM change it to hit to the body; its an extension to already existing fix in 3.22 for left hand - now its applies also for right hand
+[3.3.46] Hand hit box fix - if hands are in front of body and game somehow badly interprets it as hit to the hand, PAM change it to hit to the body; its an extension to already existing fix in 3.22 for left hand - now its applies also for right hand replaced with [3.3.??]
[3.3.47] Torso hitbox fix; lower torso hitbox (pelvis area) is slightly enlarged as workaround for bad hitbox registration between torso_lower and right/left_leg_upper; this change effectively applies only for rifles, because other weapons has the same damage for torso_lower and right/left_leg_upper
[3.3.48] Consistent shotgun - this is the new name for new shotgun; this shotgun fixes close range hits; it replaces rebalanced shotgun
[3.3.49] Ladder weapon bug fix - silent use of ladder is no more possible; its a situation when you double scroll your weapon, hold fire button and use a ladder replaced via 3.3.68
@@ -961,12 +1018,19 @@ Added posibility to call bash mode from menu.
- between halfs: 2min (previously 5min)
- between halfs at overtime: 1min (previously 5min)
- for LAN mode, there will be no time limits between halfs and maps
-[3.3.68] Climbing sound fix
+[3.3.68] Climbing sound fix
- If you climb ladder or wall, the "weapon switch" sound was made only for 1st person player, it was silent for other players
- Now the sound matches equally, so you hear exactly what other players hear (meaning if you climb ladder or wall, "weapon switch" sound is made for everybody)
- If you want to silently climb ladder or wall, you need to hold weapon down by scrolling down 2x and hold left mouse
- - This fix replaces previous [3.3.49] "Ladder weapon fix" in 3.30
+ - This fix replaces previous [3.3.49] "Ladder weapon fix" in 3.30 replaced with 3.3.70
[3.3.69] SD voiceover sound "Move in!" is now played only if player is not moving (replaces [3.3.62])
+[3.3.70] Climbing sound fix (replaces [3.3.68])
+ - restored original behavior that was affected by [3.3.68] and [3.3.49]
+ - disabling the weapon switch sound that is played only in 1st point of view
+ - meaning that everybody hear the same
+
+
+
#### 3.4 Other
diff --git a/images/gameservers.png b/images/gameservers.png
deleted file mode 100644
index a2b4876..0000000
Binary files a/images/gameservers.png and /dev/null differ
diff --git a/images/hand_hitbox_fix.png b/images/hand_hitbox_fix.png
new file mode 100644
index 0000000..462e410
Binary files /dev/null and b/images/hand_hitbox_fix.png differ
diff --git a/images/hitbox_hand2.png b/images/hitbox_hand2.png
deleted file mode 100644
index 7da2a0e..0000000
Binary files a/images/hitbox_hand2.png and /dev/null differ
diff --git a/images/round_report.png b/images/round_report.png
index 48eed7a..1fb4073 100644
Binary files a/images/round_report.png and b/images/round_report.png differ
diff --git a/images/scoreboard_columns.png b/images/scoreboard_columns.png
index 4bf9de8..43e6e5c 100644
Binary files a/images/scoreboard_columns.png and b/images/scoreboard_columns.png differ
diff --git a/images/spectator_esp.png b/images/spectator_esp.png
index b27057a..e98cfbe 100644
Binary files a/images/spectator_esp.png and b/images/spectator_esp.png differ
diff --git a/source/default_mp.cfg b/source/default_mp.cfg
index cea4aee..ab451ca 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.31" // ZPAM_RENAME
+sets _zpam "3.32" // ZPAM_RENAME
diff --git a/source/images/stance_crouch_back.iwi b/source/images/stance_crouch_back.iwi
new file mode 100644
index 0000000..bb74e61
Binary files /dev/null and b/source/images/stance_crouch_back.iwi differ
diff --git a/source/images/stance_crouch_front.iwi b/source/images/stance_crouch_front.iwi
new file mode 100644
index 0000000..b820a97
Binary files /dev/null and b/source/images/stance_crouch_front.iwi differ
diff --git a/source/images/stance_crouch_left.iwi b/source/images/stance_crouch_left.iwi
new file mode 100644
index 0000000..278153f
Binary files /dev/null and b/source/images/stance_crouch_left.iwi differ
diff --git a/source/images/stance_crouch_right.iwi b/source/images/stance_crouch_right.iwi
new file mode 100644
index 0000000..92dafcc
Binary files /dev/null and b/source/images/stance_crouch_right.iwi differ
diff --git a/source/images/stance_prone_back.iwi b/source/images/stance_prone_back.iwi
new file mode 100644
index 0000000..43fe3b3
Binary files /dev/null and b/source/images/stance_prone_back.iwi differ
diff --git a/source/images/stance_prone_front.iwi b/source/images/stance_prone_front.iwi
new file mode 100644
index 0000000..43fe3b3
Binary files /dev/null and b/source/images/stance_prone_front.iwi differ
diff --git a/source/images/stance_prone_left.iwi b/source/images/stance_prone_left.iwi
new file mode 100644
index 0000000..9f548c2
Binary files /dev/null and b/source/images/stance_prone_left.iwi differ
diff --git a/source/images/stance_prone_right.iwi b/source/images/stance_prone_right.iwi
new file mode 100644
index 0000000..5babb08
Binary files /dev/null and b/source/images/stance_prone_right.iwi differ
diff --git a/source/images/stance_stand_back.iwi b/source/images/stance_stand_back.iwi
new file mode 100644
index 0000000..3361ee1
Binary files /dev/null and b/source/images/stance_stand_back.iwi differ
diff --git a/source/images/stance_stand_front.iwi b/source/images/stance_stand_front.iwi
new file mode 100644
index 0000000..47349bc
Binary files /dev/null and b/source/images/stance_stand_front.iwi differ
diff --git a/source/images/stance_stand_left.iwi b/source/images/stance_stand_left.iwi
new file mode 100644
index 0000000..d502a7d
Binary files /dev/null and b/source/images/stance_stand_left.iwi differ
diff --git a/source/images/stance_stand_right.iwi b/source/images/stance_stand_right.iwi
new file mode 100644
index 0000000..a917c03
Binary files /dev/null and b/source/images/stance_stand_right.iwi differ
diff --git a/source/maps/mp/gametypes/_callbacksetup.gsc b/source/maps/mp/gametypes/_callbacksetup.gsc
index 3b45ef0..6ef64a3 100644
--- a/source/maps/mp/gametypes/_callbacksetup.gsc
+++ b/source/maps/mp/gametypes/_callbacksetup.gsc
@@ -155,7 +155,7 @@ CodeCallback_PlayerConnect()
self.pers["antilagTimeOffset"] = 0;
self thread maps\mp\gametypes\global\events::notifyConnecting();
-
+
// Wait here until player is fully connected
self waittill("begin");
@@ -268,48 +268,172 @@ CodeCallback_PlayerDamage(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath
damageFeedback = 1;
- /*
- // Debug angle
- if (isDefined(sWeapon) && isDefined(sHitLoc) && isDefined(eAttacker) && isPlayer(eAttacker))
+
+ // Save info about hits
+ self_num = self getEntityNumber();
+ if (isDefined(eAttacker) && isPlayer(eAttacker))
{
- angleDiff = angleDiff(self, eAttacker);
- iprintln("Hit angle:"+anglediff+"");
+ // Create variable to hold hit data
+ if (!isDefined(eAttacker.hitData))
+ eAttacker.hitData = [];
+ // Because we can hit multiple players in same time (multikill), we need to save it according to players
+ if (!isDefined(eAttacker.hitData[self_num]))
+ {
+ eAttacker.hitData[self_num] = spawnstruct();
+ eAttacker.hitData[self_num].id = 0; // inited to 0, but will be incremented. 1 then means first bullet
+ eAttacker.hitData[self_num].adjustedBy = ""; // string telling if hit was adjusted by FIXes
+ eAttacker.hitData[self_num].damage = 0;
+ eAttacker.hitData[self_num].damage_comulated = 0;
+ }
+ eAttacker.hitData[self_num].id++;
+
+ self thread hitDataAutoRestart(eAttacker, self_num);
}
- */
+
+
+ debug = 0; // 1 = print to console
+
// Hitbox left and right hand fix
if (level.scr_hitbox_hand_fix &&
- isDefined(sWeapon) && isDefined(sHitLoc) && isDefined(eAttacker) && isPlayer(eAttacker))
+ isDefined(sWeapon) && isDefined(sHitLoc) && isDefined(eAttacker) && isPlayer(eAttacker) && eAttacker != self)
{
- // Player is in ads and is shoted to left arm with rifle or scope
- if (self playerAds() > 0.5 &&
- (sWeapon == "kar98k_mp" || sWeapon == "enfield_mp" || sWeapon == "mosin_nagant_mp" ||
- sWeapon == "springfield_mp" || sWeapon == "enfield_scope_mp" || sWeapon == "kar98k_sniper_mp" || sWeapon == "mosin_nagant_sniper_mp"))
+ correctWeapon = (sMeansOfDeath == "MOD_RIFLE_BULLET" && ( // This will ignore bash
+ sWeapon == "kar98k_mp" || sWeapon == "enfield_mp" || sWeapon == "mosin_nagant_mp" ||
+ sWeapon == "springfield_mp" || sWeapon == "enfield_scope_mp" || sWeapon == "kar98k_sniper_mp" || sWeapon == "mosin_nagant_sniper_mp"));
+ damageOk = (iDamage < 100);
+ bodyOrHeadVisible = false;
+ distanceOK = false;
+ applyFix = false;
+
+ if (correctWeapon && damageOk)
{
- // If players are looking to each other, make shot to arm a kill
distance = distance(self.origin, eAttacker.origin);
- angleDiff = angleDiff(self, eAttacker);
+ distanceOK = (distance > 200);
+
+ if (distanceOK)
+ {
+ // Head or pelvis is visible to player
+ bodyOrHeadVisible = eAttacker maps\mp\gametypes\global\player::isPlayerInSight(self);
+
+ if (bodyOrHeadVisible)
+ {
+ applyFix = true;
+ }
+ }
+ }
+
+
+ // Hit with rifle or scope
+ if (level.debug_handhitbox || applyFix)
+ {
+ // Define box around head tag tag will be used to determine, if hit location is inside this box and it should be a kill
+ boxBack = 8; // _________ // _________
+ boxFront = 30; // | -[]- | Back // | _[]_ | Top
+ boxLeft = 9; // | | | Left [Head] Right // | || || | Left [Head] Right
+ boxRight = 9; // | | Front // |__| |___| Down
+ boxUp = 8; // |________| ^ // ||
+ boxDown = 14; // Top view Enemy // Front view
+ if (self.isMoving) // if player is moving, make the box bigger so it will compensate poor hitboxes
+ {
+ boxLeft = 11;
+ boxRight = 11;
+ }
+ stance = self maps\mp\gametypes\global\player::getStance(); // prone crouch stand
+ if (stance == "prone")
+ {
+ boxDown = 8;
+ }
+ attacker_eye = eAttacker maps\mp\gametypes\global\player::getEyeOrigin();
+
+ // Debug the box
+ if (level.debug_handhitbox)
+ {
+ if (!isDefined(self.hit)) self.hit = [];
+ for (i = 0; i < 9; i++)
+ {
+ if (!isDefined(self.hit[i]))
+ {
+ self.hit[i] = spawn("script_origin",(0,0,0));
+ self.hit[i].waypoint_color = (1, 0, 0);
+ self.hit[i] thread maps\mp\gametypes\global\developer::showWaypoint();
+ }
+ }
+ //angles = eAttacker getPlayerAngles();
+ angles = vectortoangles(self.headTag getOrigin() - attacker_eye);
+
+ right = anglestoright(angles);
+ up = anglestoup(angles);
+ forward = anglestoforward(angles);
+
+ pointFront = (forward[0]*(boxFront*-1), forward[1]*(boxFront*-1), forward[2]*(boxFront*-1));
+ pointBack = (forward[0]*boxBack, forward[1]*boxBack, forward[2]*boxBack);
+ pointRight = (right[0]*boxRight, right[1]*boxRight, right[2]*boxRight);
+ pointLeft = (right[0]*(boxLeft*-1), right[1]*(boxLeft*-1), right[2]*(boxLeft*-1));
+ pointUp = (up[0]*boxUp, up[1]*boxUp, up[2]*boxUp);
+ pointDown = (up[0]*(boxDown*-1), up[1]*(boxDown*-1), up[2]*(boxDown*-1));
+
+ self.hit[0].origin = (self.headTag getOrigin()) + pointFront + pointRight + pointUp;
+ self.hit[1].origin = (self.headTag getOrigin()) + pointFront + pointRight + pointDown;
+ self.hit[2].origin = (self.headTag getOrigin()) + pointFront + pointLeft + pointUp;
+ self.hit[3].origin = (self.headTag getOrigin()) + pointFront + pointLeft + pointDown;
+
+ self.hit[4].origin = (self.headTag getOrigin()) + pointBack + pointRight + pointUp;
+ self.hit[5].origin = (self.headTag getOrigin()) + pointBack + pointRight + pointDown;
+ self.hit[6].origin = (self.headTag getOrigin()) + pointBack + pointLeft + pointUp;
+ self.hit[7].origin = (self.headTag getOrigin()) + pointBack + pointLeft + pointDown;
+
+ self.hit[8].origin = vPoint;
+ }
- // Left arm
- if (distance > 200 && (
- (anglediff > 0 && anglediff < 30 && sHitLoc == "left_hand") ||
- (anglediff > -20 && anglediff < 25 && sHitLoc == "left_arm_lower")
- ) && self.angles[0] > -65 && self.angles[0] < 45)
+ //angles = eAttacker getPlayerAngles();
+ angles = vectortoangles(self.headTag getOrigin() - attacker_eye);
+ rotationMatrix[0] = anglestoforward(angles); // normalized FORWARD vector of the box
+ rotationMatrix[1] = anglestoright(angles); // normalized RIGHT vector of the box
+ rotationMatrix[2] = anglestoup(angles); // normalized UP vector of the box
+
+ vectorToPoint = VectorNormalize(vPoint - self.headTag getOrigin()); // vector pointing from the center of the box to the hit location point
+ rotatedVectorToPoint[0] = VectorDot(rotationMatrix[0], vectorToPoint);
+ rotatedVectorToPoint[1] = VectorDot(rotationMatrix[1], vectorToPoint) * -1; // idk why, but Y needs to be fliped (propably because we have right vector, but Y is pointing left)
+ rotatedVectorToPoint[2] = VectorDot(rotationMatrix[2], vectorToPoint);
+
+ // Get back the hit location but now its aligned to coordinate grid, so we can do simple box checks if point is inside
+ dist = distance(vPoint, self.headTag getOrigin());
+ x = rotatedVectorToPoint[0] * dist;
+ y = rotatedVectorToPoint[1] * dist;
+ z = rotatedVectorToPoint[2] * dist;
+
+ // Hit location is inside box
+ if ((x < boxBack && x > boxFront * -1) && (y < boxLeft && y > boxRight * -1) && (z < boxUp && z > boxDown * -1))
{
- if (level.debug_handhitbox) iprintln("^1Hand hitbox fix - making damage to "+sHitLoc+" as kill (angle:"+anglediff+")");
- //println("### Hand hitbox fix - making damage to "+sHitLoc+" as kill (angle:"+anglediff+")"); // EYZA_DEBUG
- iDamage = 135;
+ if (level.debug_handhitbox)
+ {
+ if (!correctWeapon) eAttacker iprintln("^3Hand hitbox fix - inside, but this weapon is ignored");
+ else if (!damageOk) eAttacker iprintln("^3Hand hitbox fix - inside, but this is already determined as KILL");
+ else if (!distanceOK) eAttacker iprintln("^3Hand hitbox fix - inside, but your too close to enemy");
+ else if (!bodyOrHeadVisible) eAttacker iprintln("^3Hand hitbox fix - inside, but body and head is not visible");
+ else eAttacker iprintln("^1Hand hitbox fix - hit is inside the box, changing to KILL!");
+ }
+ if (applyFix)
+ {
+ if (debug) println("^1Hand hitbox fix - making damage to "+sHitLoc+" as kill for " + eAttacker.name); // EYZA_DEBUG
+ eAttacker.hitData[self_num].adjustedBy = "hand_hitbox_fix";
+ iDamage = 100;
+ }
}
- // Right arm
- if (distance > 200 && (
- (anglediff > 0 && anglediff < 75 && sHitLoc == "right_hand") ||
- (anglediff > 45 && anglediff < 100 && sHitLoc == "right_arm_lower")
- ) && self.angles[0] > -65 && self.angles[0] < 45)
+ else
{
- if (level.debug_handhitbox) iprintln("^1Hand hitbox fix - making damage to "+sHitLoc+" as kill (angle:"+anglediff+")");
- //println("### Hand hitbox fix - making damage to "+sHitLoc+" as kill (angle:"+anglediff+")"); // EYZA_DEBUG
- iDamage = 135;
+ if (level.debug_handhitbox) eAttacker iprintln("^9Hand hitbox fix - hit is outside the box");
}
+
+ /*
+ self.hit[7].origin = (self.headTag getOrigin()) + (0, 150, 0) + (0, 0, 0);
+ self.hit[8].origin = (self.headTag getOrigin()) + (0, 150, 0) + (boxBack, 0, 0);
+ self.hit[9].origin = (self.headTag getOrigin()) + (0, 150, 0) + (0, boxLeft, 0);
+ self.hit[10].origin = (self.headTag getOrigin()) + (0, 150, 0) + (0, boxRight * -1, 0);
+ self.hit[11].origin = (self.headTag getOrigin()) + (0, 150, 0) + (boxFront * -1, 0, 0);
+ self.hit[12].origin = (self.headTag getOrigin()) + (0, 150, 0) + Transform;
+ */
}
}
@@ -319,23 +443,43 @@ CodeCallback_PlayerDamage(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath
{
// Bigger torso hitbox
// This change efectively applies only for rifles, because other weapons has the same damage for torso_lower and right/left_leg_upper
- if (sWeapon == "kar98k_mp" || sWeapon == "enfield_mp" || sWeapon == "mosin_nagant_mp")
+ correctWeapon = (sMeansOfDeath == "MOD_RIFLE_BULLET" && ( // This will ignore bash
+ sWeapon == "kar98k_mp" || sWeapon == "enfield_mp" || sWeapon == "mosin_nagant_mp"));
+
+ correctHitLoc = (sHitLoc == "left_leg_upper" || sHitLoc == "right_leg_upper");
+ dist = 0;
+ distOk = false;
+ applyFix = false;
+
+ if (correctWeapon && correctHitLoc)
{
- if (sHitLoc == "left_leg_upper" || sHitLoc == "right_leg_upper")
- {
- dist = distance(self.pelvisTag getOrigin(), vPoint);
+ dist = distance(self.pelvisTag getOrigin(), vPoint);
+ distOk = (dist <= 16.5); // Distance between pelvis and knee is around 21
- // Distance between pelvis and knee is around 21
- if (dist < 15.0)
- {
- if (level.debug_torsohitbox) iprintln("^1Torso hitbox fix - adjusted damage from " + iDamage + " to 135");
- //println("### Torso hitbox fix - adjusted damage from " + iDamage + " to 135 for " + eAttacker.name); // EYZA_DEBUG
- iDamage = 135;
- }
+ if (distOk)
+ applyFix = true;
+ }
+
+ if (applyFix)
+ {
+ if (level.debug_torsohitbox)
+ {
+ eAttacker iprintln("^1Torso hitbox fix - adjusted damage from " + iDamage + " to 100");
}
+ if (debug) println("### Torso hitbox fix - adjusted damage from " + iDamage + " to 100 for " + eAttacker.name); // EYZA_DEBUG
+ eAttacker.hitData[self_num].adjustedBy = "torso_hitbox_fix";
+ iDamage = 100;
+ }
+ else if (level.debug_torsohitbox)
+ {
+ if (!correctWeapon) eAttacker iprintln("^9Torso hitbox fix - no fix, because this weapon is ignored");
+ else if (!correctHitLoc) eAttacker iprintln("^9Torso hitbox fix - no fix, because hit location is not LEG_UPPER");
+ else if (!distOk) eAttacker iprintln("^9Torso hitbox fix - no fix, because hit distance " + int(dist*10)/10 + " > 16.5");
}
}
+
+
// Consistent shotgun
if (level.scr_shotgun_consistent && isDefined(eAttacker) && isPlayer(eAttacker) && isDefined(sHitLoc) && isDefined(sMeansOfDeath) && isDefined(sHitLoc) &&
isDefined(sWeapon) && sWeapon == "shotgun_mp" && sMeansOfDeath == "MOD_PISTOL_BULLET")
@@ -345,30 +489,13 @@ CodeCallback_PlayerDamage(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath
// In the same frame, all pellets that hit the target are called one by one
// If one pellet kills the enemy, all other pellets are not called, because the player is already killed and is not part of world anymore
- // Create variable to hold info about shotgun hits
- if (!isDefined(eAttacker.shotgunHit))
- {
- eAttacker.shotgunHit = [];
- eAttacker thread shotgunCounterAutoRestart(); // will delete eAttacker.shotgunHit after this frame ends
- }
-
- // Because we can hit multiple players in same time (multikill), we need to save it according to players
- self_num = self getEntityNumber();
- if (!isDefined(eAttacker.shotgunHit[self_num]))
- {
- eAttacker.shotgunHit[self_num] = spawnstruct();
- eAttacker.shotgunHit[self_num].id = 0; // inited to 0, but will be incremented. 1 then means first pellet
- }
- eAttacker.shotgunHit[self_num].id++;
-
-
// count distance
dist = distance(self getOrigin(), eAttacker getOrigin());
// Make sure pellets do only once a feedback damage
// This is the first pellet
- if (eAttacker.shotgunHit[self_num].id == 1)
+ if (eAttacker.hitData[self_num].id == 1)
damageFeedback = 1;
else
damageFeedback = 0;
@@ -383,19 +510,14 @@ CodeCallback_PlayerDamage(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath
if (sHitLoc == "left_hand" || sHitLoc == "left_arm_lower" || sHitLoc == "right_hand" || sHitLoc == "right_arm_lower" ||
sHitLoc == "left_foot" || sHitLoc == "left_leg_lower" || sHitLoc == "right_foot" || sHitLoc == "right_leg_lower")
{
- eye = eAttacker maps\mp\gametypes\global\player::getEyeOrigin();
-
- trace = Bullettrace(eye, self.headTag getOrigin(), true, eAttacker);
- headVisible = isDefined(trace["entity"]) && trace["entity"] == self;
-
- trace = Bullettrace(eye, self.pelvisTag getOrigin(), true, eAttacker);
- pelvisVisible = isDefined(trace["entity"]) && trace["entity"] == self;
-
- //println("### Consistent shotgun: upcoming hit is to leg or hand | headVisible:" + headVisible + " | pelvisVisible:" + pelvisVisible); // EYZA_DEBUG
+ // Head or pelvis is visible to player
+ bodyOrHeadVisible = eAttacker maps\mp\gametypes\global\player::isPlayerInSight(self);
// Head and pelvis is not at sight (only hand or lags are visible to player) - this should be a hit only
- if (!headVisible && !pelvisVisible)
+ if (!bodyOrHeadVisible)
isKill = false;
+
+ if (debug) println("### Consistent shotgun: upcoming hit is to leg or hand | bodyOrHeadVisible:" + bodyOrHeadVisible); // EYZA_DEBUG
}
@@ -404,14 +526,16 @@ CodeCallback_PlayerDamage(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath
iDamage = 100;
damageFeedback = 2; // Do big damage feedback, because this bullet kills the player and the others are canceled due to this
if (level.debug_shotgun) eAttacker iprintln("^1Distance " + int(dist) + " | close range 0-250 | KILL");
- //println("### Consistent shotgun: attacker:"+eAttacker.name+" | victim:"+self.name+" | distance:" + int(dist) + " | hitLoc:" + sHitLoc + " | close range 0-250 | KILL"); // EYZA_DEBUG
+ if (debug) println("### Consistent shotgun: attacker:"+eAttacker.name+" | victim:"+self.name+" | distance:" + int(dist) + " | hitLoc:" + sHitLoc + " | close range 0-250 | KILL"); // EYZA_DEBUG
+ eAttacker.hitData[self_num].adjustedBy = "consistent_shotgun_1_kill"; // Range 1, kill
}
else
{
// Scale the damage based on distance
iDamage = damageScale(dist, 0, 250, 100, 50); //distance, distStart, distEnd, hpStart, hpEnd
if (level.debug_shotgun) eAttacker iprintln("^1Distance " + int(dist) + " | close range 0-250 | ^3hit to hand or leg");
- //println("### Consistent shotgun: attacker:"+eAttacker.name+" | victim:"+self.name+" | distance:" + int(dist) + " | hitLoc:" + sHitLoc + " | close range 0-250 | hit to hand or leg"); // EYZA_DEBUG
+ if (debug) println("### Consistent shotgun: attacker:"+eAttacker.name+" | victim:"+self.name+" | distance:" + int(dist) + " | hitLoc:" + sHitLoc + " | close range 0-250 | hit to hand or leg"); // EYZA_DEBUG
+ eAttacker.hitData[self_num].adjustedBy = "consistent_shotgun_1_hit"; // Range 1, hit only, because head and body is not visible
}
}
@@ -421,9 +545,9 @@ CodeCallback_PlayerDamage(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath
// Scale the damage based on distance
iDamage = damageScale(dist, 250, 384, 100, 50); //distance, distStart, distEnd, hpStart, hpEnd
- if (level.debug_shotgun) eAttacker iprintln("^3Distance " + int(dist) + " | mid range 250-384 | " + iDamage + "hp damage (pellet id: " + eAttacker.shotgunHit[self_num].id + ")");
-
- //println("### Consistent shotgun: attacker:"+eAttacker.name+" | victim:"+self.name+" | distance:" + int(dist) + " | hitLoc:" + sHitLoc + " | mid range 250-384 | damage:" + iDamage + " | pelletId:" + eAttacker.shotgunHit[self_num].id); // EYZA_DEBUG
+ if (level.debug_shotgun) eAttacker iprintln("^3Distance " + int(dist) + " | mid range 250-384 | " + iDamage + "hp damage (pellet id: " + eAttacker.hitData[self_num].id + ")");
+ if (debug) println("### Consistent shotgun: attacker:"+eAttacker.name+" | victim:"+self.name+" | distance:" + int(dist) + " | hitLoc:" + sHitLoc + " | mid range 250-384 | damage:" + iDamage + " | pelletId:" + eAttacker.hitData[self_num].id); // EYZA_DEBUG
+ eAttacker.hitData[self_num].adjustedBy = "consistent_shotgun_2"; // Range 2
}
// Range 384-500 (3 pellets needed for kill)
@@ -432,23 +556,23 @@ CodeCallback_PlayerDamage(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath
// Scale the damage based on distance
iDamage = damageScale(dist, 384, 500, 50, 34); //distance, distStart, distEnd, hpStart, hpEnd
- if (level.debug_shotgun) eAttacker iprintln("^4Distance " + int(dist) + " | mid range 384-500 | " + iDamage + "hp damage (pellet id: " + eAttacker.shotgunHit[self_num].id + ")");
-
- //println("### Consistent shotgun: attacker:"+eAttacker.name+" | victim:"+self.name+" | distance:" + int(dist) + " | hitLoc:" + sHitLoc + " | mid range 384-500 | damage:" + iDamage + " | pelletId:" + eAttacker.shotgunHit[self_num].id); // EYZA_DEBUG
+ if (level.debug_shotgun) eAttacker iprintln("^4Distance " + int(dist) + " | mid range 384-500 | " + iDamage + "hp damage (pellet id: " + eAttacker.hitData[self_num].id + ")");
+ if (debug) println("### Consistent shotgun: attacker:"+eAttacker.name+" | victim:"+self.name+" | distance:" + int(dist) + " | hitLoc:" + sHitLoc + " | mid range 384-500 | damage:" + iDamage + " | pelletId:" + eAttacker.hitData[self_num].id); // EYZA_DEBUG
+ eAttacker.hitData[self_num].adjustedBy = "consistent_shotgun_3"; // Range 3
}
// Range 500 - 800 (only 1 pellet is counted, so atleast 3 shots are needed for kill)
else
{
// This is the first pellet
- if (eAttacker.shotgunHit[self_num].id == 1)
+ if (eAttacker.hitData[self_num].id == 1)
{
// Scale the damage based on distance
iDamage = damageScale(dist, 500, 800, 34, 0); //distance, distStart, distEnd, hpStart, hpEnd
if (level.debug_shotgun) eAttacker iprintln("^9Distance " + int(dist) + " | far range 500-800 | linear " + iDamage + "hp damage");
-
- //println("### Consistent shotgun: attacker:"+eAttacker.name+" | victim:"+self.name+" | distance:" + int(dist) + " | hitLoc:" + sHitLoc + " | far range 500-800 | linear damage:" + iDamage); // EYZA_DEBUG
+ if (debug) println("### Consistent shotgun: attacker:"+eAttacker.name+" | victim:"+self.name+" | distance:" + int(dist) + " | hitLoc:" + sHitLoc + " | far range 500-800 | linear damage:" + iDamage); // EYZA_DEBUG
+ eAttacker.hitData[self_num].adjustedBy = "consistent_shotgun_4"; // Range 4
}
else
{
@@ -466,10 +590,16 @@ CodeCallback_PlayerDamage(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath
}
// Prevents a pistol from killing in one shot if active
if (level.prevent_single_shot_pistol && isDefined(sWeapon) && maps\mp\gametypes\_weapons::isPistol(sWeapon) && isdefined(sHitLoc) && sHitLoc == "head")
- iDamage = int(iDamage * .85);
+ iDamage = int(iDamage * .85);
// Prevents a ppsh from killing in one shot if active
if (level.prevent_single_shot_ppsh && isDefined(sWeapon) && sWeapon == "ppsh_mp" && isdefined(sHitLoc) && sHitLoc == "head")
- iDamage = int(iDamage * .9);
+ iDamage = int(iDamage * .9);
+
+
+
+ // Save affected damage value
+ if (isDefined(eAttacker) && isPlayer(eAttacker))
+ eAttacker.hitData[self_num].damage = iDamage;
//println("##################### " + "notifyDamaging");
@@ -477,6 +607,12 @@ CodeCallback_PlayerDamage(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath
ret = maps\mp\gametypes\global\events::notifyDamaging(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset);
if (ret) return;
+
+ // Save affected damage value
+ if (isDefined(eAttacker) && isPlayer(eAttacker))
+ eAttacker.hitData[self_num].damage_comulated += iDamage;
+
+
//println("##################### " + "notifyDamage");
// Call onPlayerDamaged event
maps\mp\gametypes\global\events::notifyDamage(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset);
@@ -493,27 +629,34 @@ CodeCallback_PlayerDamage(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath
}
}
-shotgunCounterAutoRestart()
+hitDataAutoRestart(eAttacker, id)
{
- waittillframeend;
- self.shotgunHit = undefined;
-}
+ //self endon("disconnect");
+ eAttacker endon("disconnect");
-angleDiff(player, eAttacker)
-{
- myAngle = player.angles[1]; // -180 <-> +180
- myAngle += 180; // flip direction, now in range 0 <-> 360
- if (myAngle > 180) myAngle -= 360; // back to range -180 <-> +180
+ self notify("hitDataAutoRestart_end");
+ self endon("hitDataAutoRestart_end");
- enemyAngle = eAttacker.angles[1];
+ // Reset data related to a single frame only
+ waittillframeend;
+ waittillframeend;
+ waittillframeend;
+ waittillframeend;
+ eAttacker.hitData[id].id = 0;
+ eAttacker.hitData[id].damage = 0;
+ eAttacker.hitData[id].adjustedBy = "";
- anglediff = myAngle - enemyAngle;
- if (anglediff > 180) anglediff -= 360;
- else if (anglediff < -180) anglediff += 360;
+ // Wait 5 sec if player is still alive
+ for(i = 0; i < 5; i++)
+ {
+ if (!isDefined(self) || !isAlive(self))
+ break;
- //iprintln(anglediff);
+ wait level.fps_multiplier * 1;
+ }
- return anglediff;
+ // Remove the rest of the data
+ eAttacker.hitData[id] = undefined;
}
damageScale(dist, distStart, distEnd, hpStart, hpEnd)
diff --git a/source/maps/mp/gametypes/_climbing_sound_fix.gsc b/source/maps/mp/gametypes/_climbing_sound_fix.gsc
index cd7d30f..d639e64 100644
--- a/source/maps/mp/gametypes/_climbing_sound_fix.gsc
+++ b/source/maps/mp/gametypes/_climbing_sound_fix.gsc
@@ -1,23 +1,23 @@
#include maps\mp\gametypes\global\_global;
/*
-If you climb ladder or wall, the "weapon switch" sound is made only for 1st person player, its silent for other players
-If you hide weapon and you climb ladder or obstacle, no "weapon switch" sound is played, its silent for everybody
+If you climb ladder or wall, the "weapon switch" sound was made only for 1st person player, it was silent for other players.
+The sound is now removed completly - meaning if you climb ladder or wall, "weapon switch" sound is not played at all - same for everybody
PAM disabled playing weapon sound change in soundaliases and this script is plaing the sound manually
-| -----------------------------------------------------------------------------------------------
-| PAM | | Weapon normal state | Weapon holded down |
-| PAM | Action | Ladder | Climb wall | Ladder entering | Climb wall |
-| --------------|---------------|---------------|---------------|-----------------|-------------|
-| Original | Client | sound | sound | - | - |
-| Original | Others | - | - | - | - |
-| --------------|---------------|---------------|---------------|-----------------|--------------
-| zPAM3.31 | Client | sound | sound | - | - |
-| zPAM3.31 | Others | sound | sound | - | - |
-| -----------------------------------------------------------------------------------------------
-
-If players want to silently climb ladder or wall, they need to bug weapon by scrolling down 2x and hold left mouse (weapon holded down)
+| --------------------------------------------------------------|
+| PAM | Action | Ladder | Climb wall |
+| --------------|---------------|---------------|---------------|
+| Original | Client | sound | sound |
+| Original | Others | - | - |
+| --------------|---------------|---------------|---------------|
+| zPAM3.32 | Client | - | - |
+| zPAM3.32 | Others | - | - |
+| --------------------------------------------------------------|
+
+This sound is played only if you regularly and intentionally switch the weapon
+
*/
@@ -50,7 +50,7 @@ bug_fix()
current = self getcurrentweapon();
alive = self.sessionstate == "playing";
- if (alive && aliveLast && current != currentLast && currentLast != "none" && weapon1 == weapon1Last && weapon2 == weapon2Last)
+ if (alive && aliveLast && current != currentLast && currentLast != "none" && current != "none" && weapon1 == weapon1Last && weapon2 == weapon2Last)
{
//self iprintln("^2Climb sound fix");
diff --git a/source/maps/mp/gametypes/_killcam.gsc b/source/maps/mp/gametypes/_killcam.gsc
index df8ac41..b52649e 100644
--- a/source/maps/mp/gametypes/_killcam.gsc
+++ b/source/maps/mp/gametypes/_killcam.gsc
@@ -77,7 +77,7 @@ killcam(attackerNum, pastTime, length, offsetTime, respawn, isReplay)
self.archivetime = 0;
self.psoffsettime = 0;
- self.killcam = false;
+ self.killcam = undefined;
return;
}
diff --git a/source/maps/mp/gametypes/_language.gsc b/source/maps/mp/gametypes/_language.gsc
new file mode 100644
index 0000000..f5c22e7
--- /dev/null
+++ b/source/maps/mp/gametypes/_language.gsc
@@ -0,0 +1,82 @@
+#include maps\mp\gametypes\global\_global;
+
+init()
+{
+ addEventListener("onConnected", ::onConnected);
+}
+
+onConnected()
+{
+ if (!isDefined(self.pers["language_orig"]))
+ self.pers["language_orig"] = -1;
+ if (!isDefined(self.pers["language_setting"]))
+ self.pers["language_setting"] = -1;
+ // English by default
+ if (!isDefined(self.pers["language"]))
+ self.pers["language"] = 0;
+}
+
+/*
+ // Localization
+ execOnDvarStringValue ui_language 0 "openscriptmenu -1 key_0"; // English
+ execOnDvarStringValue ui_language 1 "openscriptmenu -1 key_1"; // French
+ execOnDvarStringValue ui_language 2 "openscriptmenu -1 key_2"; // German
+ execOnDvarStringValue ui_language 6 "openscriptmenu -1 key_6"; // Russian
+ execOnDvarStringValue ui_language 7 "openscriptmenu -1 key_7"; // Polish
+ execOnDvarStringValue ui_language 14 "openscriptmenu -1 key_14"; // Czech
+ execOnDvarStringValue ui_language 15 "openscriptmenu -1 key_15"; // Czech
+*/
+
+// Called from OnConnect menu - value of original loc_language cvar
+// If known languages are not recognized, -1 is as default
+saveOriginalLanguage(lang)
+{
+ if (lang == "0") self.pers["language_orig"] = 0;
+ else if (lang == "1") self.pers["language_orig"] = 1;
+ else if (lang == "2") self.pers["language_orig"] = 2;
+ else if (lang == "6") self.pers["language_orig"] = 6;
+ else if (lang == "7") self.pers["language_orig"] = 7;
+ else if (lang == "14") self.pers["language_orig"] = 14;
+ else if (lang == "15") self.pers["language_orig"] = 15;
+ else self.pers["language_orig"] = -1;
+
+ // Parse language
+ self iprintln("Original language: " + self.pers["language_orig"]);
+}
+
+// Called from quick settings menu
+toggle()
+{
+ if (self.pers["language_setting"] == -1) select(0);
+ else if (self.pers["language_setting"] == 0) select(1);
+ else if (self.pers["language_setting"] == 1) select(2);
+ else if (self.pers["language_setting"] == 2) select(6);
+ else if (self.pers["language_setting"] == 6) select(7);
+ else if (self.pers["language_setting"] == 7) select(14);
+ else if (self.pers["language_setting"] == 14) select(15);
+ else if (self.pers["language_setting"] == 15) select(-1);
+ else select(-1);
+}
+
+// Called from quick settings menu when saved players settings are loaded
+select(lang)
+{
+ // If language is "Automatic", use original language from game
+ if (lang == -1)
+ lang = self.pers["language_orig"];
+
+ // If original language is unknown, use English
+ if (lang == -1)
+ lang = 0;
+
+ self.pers["language_setting"] = lang;
+ self.pers["language"] = lang;
+
+ self iprintln("Language switched to " + lang);
+}
+
+// Called from quick settings menu when current settings is saved
+getValue()
+{
+ return self.pers["language_setting"];
+}
diff --git a/source/maps/mp/gametypes/_menu_debug.gsc b/source/maps/mp/gametypes/_menu_debug.gsc
index c751b52..4947b2b 100644
--- a/source/maps/mp/gametypes/_menu_debug.gsc
+++ b/source/maps/mp/gametypes/_menu_debug.gsc
@@ -75,6 +75,26 @@ onMenuResponse(menu, response)
game["allies_score"] = game["axis_score"];
game["is_halftime"] = true;
}
+ if (response == "intermission")
+ {
+ level notify("round_ended");
+ level.roundended = true;
+
+ game["state"] = "intermission";
+ level notify("intermission");
+
+ // Spawn each player into intermission
+ players = getentarray("player", "classname");
+ for(i = 0; i < players.size; i++)
+ {
+ player = players[i];
+
+ player closeMenu();
+ player closeInGameMenu();
+
+ player [[level.spawnIntermission]]();
+ }
+ }
}
}
diff --git a/source/maps/mp/gametypes/_menu_rcon_map.gsc b/source/maps/mp/gametypes/_menu_rcon_map.gsc
index a0c14c5..7a6f943 100644
--- a/source/maps/mp/gametypes/_menu_rcon_map.gsc
+++ b/source/maps/mp/gametypes/_menu_rcon_map.gsc
@@ -324,11 +324,11 @@ saveSubPamMode(str)
self.pers["rcon_map_pam_league"] = str;
else if ((gametype == "sd" && (str == "mr3" || str == "mr10" || str == "mr12" || str == "mr15" || str == "20rounds")) ||
- (gametype == "dm" && (str == "15min" || str == "30min" || str == "60min" || str == "unlim")) ||
- (gametype == "tdm" && (str == "15min" || str == "30min" || str == "60min" || str == "unlim")) ||
- (gametype == "hq" && (str == "15min" || str == "30min" || str == "60min" || str == "unlim")) ||
- (gametype == "ctf" && (str == "15min" || str == "30min" || str == "60min" || str == "unlim")) ||
- (gametype == "htf" && (str == "15min" || str == "30min" || str == "60min" || str == "unlim")) ||
+ (gametype == "dm" && (str == "10min" || str == "15min" || str == "30min" || str == "60min" || str == "unlim")) ||
+ (gametype == "tdm" && (str == "10min" || str == "15min" || str == "30min" || str == "60min" || str == "unlim")) ||
+ (gametype == "hq" && (str == "10min" || str == "15min" || str == "30min" || str == "60min" || str == "unlim")) ||
+ (gametype == "ctf" && (str == "10min" || str == "15min" || str == "30min" || str == "60min" || str == "unlim")) ||
+ (gametype == "htf" && (str == "10min" || str == "15min" || str == "30min" || str == "60min" || str == "unlim")) ||
(gametype == "re" && (str == "mr3" || str == "mr10" || str == "mr12" || str == "mr15" || str == "20rounds")))
{
if (self.pers["rcon_map_pam_gamesettings"] == str)
@@ -372,11 +372,11 @@ joinSubPamModes()
{
str = self.pers["rcon_map_pam_league"];
if (self.pers["rcon_map_pam_gamesettings"] != "") str += "_" + self.pers["rcon_map_pam_gamesettings"];
+ if (self.pers["rcon_map_pam_rifle"]) str += "_rifle";
if (self.pers["rcon_map_pam_2v2"]) str += "_2v2";
if (self.pers["rcon_map_pam_russian"]) str += "_russian";
if (self.pers["rcon_map_pam_lan"]) str += "_lan";
if (self.pers["rcon_map_pam_pcw"]) str += "_pcw";
- if (self.pers["rcon_map_pam_rifle"]) str += "_rifle";
return str;
}
diff --git a/source/maps/mp/gametypes/_menu_scoreboard.gsc b/source/maps/mp/gametypes/_menu_scoreboard.gsc
index b52d1bf..1d7636b 100644
--- a/source/maps/mp/gametypes/_menu_scoreboard.gsc
+++ b/source/maps/mp/gametypes/_menu_scoreboard.gsc
@@ -3,7 +3,7 @@
/*
This script will generate scoreboard with players stats
-To make this possible, we will generate 24 lines and text will be colums where lines are separated by new line
+To make this possible, we will generate 26 lines and text will be colums where lines are separated by new line
*/
init()
@@ -23,7 +23,13 @@ onConnected()
else
self setClientCvar2("ui_scoreboard_show", "");
- self.pers["scoreboard_lines_players"] = [];
+ self.pers["scoreboard_lines_statIds"] = [];
+ if (!isDefined(self.pers["scoreboard_keepRefreshing"]))
+ self.pers["scoreboard_keepRefreshing"] = false;
+
+ // This will make sure menu is still responsible even if map is restarted (next round)
+ if (self.pers["scoreboard_keepRefreshing"])
+ self thread generatePlayerList();
}
/*
@@ -53,10 +59,11 @@ onMenuResponse(menu, response)
//self iprintln(line);
- if (!isDefined(self.pers["scoreboard_lines_players"][line]))
+ if (!isDefined(self.pers["scoreboard_lines_statIds"][line]))
return true;
- clickedPlayer = self.pers["scoreboard_lines_players"][line];
+ statsId = self.pers["scoreboard_lines_statIds"][line];
+ clickedPlayer = game["playerstats"][statsId]["player"];
if (!isPlayer(clickedPlayer))
return true;
@@ -64,7 +71,7 @@ onMenuResponse(menu, response)
//self iprintln(clickedPlayer.name);
if (!self canBeColorChanged(clickedPlayer))
- return false;
+ return true;
if (!isDefined(clickedPlayer.pers["scoreboard_color"]))
clickedPlayer.pers["scoreboard_color"] = 1;
@@ -105,46 +112,30 @@ updatePlayerLists()
}
}
-sort(players)
+sortByScore(stats)
{
// Sort score from lowest to highest
// Players with undefined stats will be lowest
- for(j = 1; j < players.size; j++)
+ for(j = 1; j < stats.size; j++)
{
for (
jj = j;
jj >= 1 && (
- !isDefined(players[jj].stats) || (isDefined(players[jj-1].stats) && isDefined(players[jj].stats) && (
- (players[jj-1].stats["score"] > players[jj].stats["score"]) ||
- (players[jj-1].stats["score"] == players[jj].stats["score"] && players[jj-1].stats["kills"] > players[jj].stats["kills"]) ||
- (players[jj-1].stats["score"] == players[jj].stats["score"] && players[jj-1].stats["kills"] == players[jj].stats["kills"] && players[jj-1].stats["deaths"] < players[jj].stats["deaths"])
- ))
+ (stats[jj-1]["score"] > stats[jj]["score"]) ||
+ (stats[jj-1]["score"] == stats[jj]["score"] && stats[jj-1]["kills"] > stats[jj]["kills"]) ||
+ (stats[jj-1]["score"] == stats[jj]["score"] && stats[jj-1]["kills"] == stats[jj]["kills"] && stats[jj-1]["deaths"] < stats[jj]["deaths"])
);
jj--)
{
- temp = players[jj];
- players[jj] = players[jj-1];
- players[jj-1] = temp;
+ temp = stats[jj];
+ stats[jj] = stats[jj-1];
+ stats[jj-1] = temp;
}
}
- return players;
+ return stats;
}
-
-sortPlayers(players)
-{
- // Find stats
- for(p = 0; p < players.size; p++)
- {
- player = players[p];
- player.stats = player maps\mp\gametypes\_player_stat::getStats();
- }
-
- return sort(players);
-}
-
-
-sortTeamsByScore()
+getTeamPlayersArraySorted()
{
teamplayers = [];
teamplayers["allies"] = [];
@@ -152,21 +143,25 @@ sortTeamsByScore()
teamplayers["spectator"] = [];
teamplayers["none"] = [];
- players = getentarray("player", "classname");
- for(p = 0; p < players.size; p++)
+ // Loop statistics of players and save them into array according to team
+ for(i = 0; i < game["playerstats"].size; i++)
{
- player = players[p];
- switch(player.sessionteam)
+ stat = game["playerstats"][i];
+ if (stat["deleted"] == false)
{
- case "allies": case "axis": case "spectator": case "none":
- teamplayers[player.sessionteam][teamplayers[player.sessionteam].size] = player;
+ switch(stat["team"])
+ {
+ case "allies": case "axis": case "spectator": case "none":
+ teamplayers[stat["team"]][teamplayers[stat["team"]].size] = stat;
+ }
}
}
+
// find stats for players and sort them by score
- teamplayers["allies"] = sortPlayers(teamplayers["allies"]);
- teamplayers["axis"] = sortPlayers(teamplayers["axis"]);
- teamplayers["spectator"] = sortPlayers(teamplayers["spectator"]);
- teamplayers["none"] = sortPlayers(teamplayers["none"]);
+ teamplayers["allies"] = sortByScore(teamplayers["allies"]);
+ teamplayers["axis"] = sortByScore(teamplayers["axis"]);
+ teamplayers["spectator"] = sortByScore(teamplayers["spectator"]);
+ teamplayers["none"] = sortByScore(teamplayers["none"]);
return teamplayers;
}
@@ -175,44 +170,35 @@ canBeColorChanged(player)
{
// Enable color change only if we are not in first readyup, only for opponent team
return !game["is_public_mode"] && !game["readyup_first_run"] && game["state"] != "intermission" &&
+ isDefined(player) &&
self.pers["team"] != player.pers["team"] &&
(self.pers["team"] == "allies" || self.pers["team"] == "axis") &&
(player.pers["team"] == "allies" || player.pers["team"] == "axis");
}
-addLine(player, name, score, kills, deaths, assists, damages, plants, defuses)
+addLine(stats, name, score, kills, deaths, assists, damages, grenades, plants, defuses)
{
- if (isDefined(player))
- self.pers["scoreboard_lines_players"][self.scoreboard.i] = player;
- else
- self.pers["scoreboard_lines_players"][self.scoreboard.i] = undefined;
-
- if (isDefined(player) && isDefined(player.pers["scoreboard_color"]) && self canBeColorChanged(player))
+ // If empty line (parameters of function are not set)
+ if (!isDefined(stats))
{
- if (player.pers["scoreboard_color"] == 1)
- self.scoreboard.names += "^1"; // red
- else if (player.pers["scoreboard_color"] == 2)
- self.scoreboard.names += "^4"; // blue
+ self.pers["scoreboard_lines_statIds"][self.scoreboard.i] = undefined;
}
-
-
-
- if (isDefined(name))
+ else
{
- // In final scoreboard show name colors, otherwise dont
- if (game["state"] != "intermission")
- name = removeColorsFromString(name);
+ // Save id of current stat into array so when the player is clicked in menu we get the stat
+ self.pers["scoreboard_lines_statIds"][self.scoreboard.i] = stats["id"];
+
self.scoreboard.names += name;
+ self.scoreboard.scores += score;
+ self.scoreboard.kills += kills;
+ self.scoreboard.deaths += deaths;
+ self.scoreboard.assists += assists;
+ self.scoreboard.damages += damages;
+ self.scoreboard.grenades += grenades;
+ self.scoreboard.plants += plants;
+ self.scoreboard.defuses += defuses;
}
- if (isDefined(score)) self.scoreboard.scores += score;
- if (isDefined(kills)) self.scoreboard.kills += kills;
- if (isDefined(deaths)) self.scoreboard.deaths += deaths;
- if (isDefined(assists)) self.scoreboard.assists += assists;
- if (isDefined(damages)) self.scoreboard.damages += damages;
- if (isDefined(plants)) self.scoreboard.plants += plants;
- if (isDefined(defuses)) self.scoreboard.defuses += defuses;
- if (isDefined(defuses)) self.scoreboard.pers["antilagTimeOffset"] += player.pers["antilagTimeOffset"] + "ms";
self.scoreboard.names += "\n^7";
self.scoreboard.scores += "\n^7";
@@ -220,9 +206,9 @@ addLine(player, name, score, kills, deaths, assists, damages, plants, defuses)
self.scoreboard.deaths += "\n^7";
self.scoreboard.assists += "\n^7";
self.scoreboard.damages += "\n^7";
+ self.scoreboard.grenades += "\n^7";
self.scoreboard.plants += "\n^7";
self.scoreboard.defuses += "\n^7";
- self.scoreboard.pers["antilagTimeOffset"] += "\n^7";
self.scoreboard.i++;
}
@@ -244,11 +230,11 @@ addTeamLine(teamname)
addEmptyLine();
}
-addPlayerLine(team, player)
+addPlayerLine(team, stats)
{
// 0=hide; 1=show line odd; 2=show line even; 3=show line hightlighted; "string"=show team name
value = "";
- if (self == player)
+ if (isPlayer(stats["player"]) && self == stats["player"])
value = "3"; // highlight
else if ((self.scoreboard.i % 2) == 0)
value = "2";
@@ -256,7 +242,7 @@ addPlayerLine(team, player)
value = "1";
//added _ means that line can be clicked
- if (self canBeColorChanged(player))
+ if (self canBeColorChanged(stats["player"]))
{
value = value + "_";
}
@@ -264,37 +250,68 @@ addPlayerLine(team, player)
self setClientCvarIfChanged("ui_scoreboard_line_"+self.scoreboard.i, value);
+ name = stats["name"];
+ // Final intermission scoreboard
+ if (game["state"] == "intermission")
+ {
+ if (stats["isConnected"])
+ name = stats["player"].name; // get actual player name with colors
+ }
+ // Ingame menu scoreboard
+ else {
+ name = removeColorsFromString(name); // remove colors
+
+ if (isPlayer(stats["player"]) && isDefined(stats["player"].pers["scoreboard_color"]) && self canBeColorChanged(stats["player"]))
+ {
+ if (stats["player"].pers["scoreboard_color"] == 1)
+ name = "^1" + name; // red
+ else if (stats["player"].pers["scoreboard_color"] == 2)
+ name = "^4" + name; // blue
+ }
+ }
+ // PLayer is disconnected
+ color = "";
+ if (stats["isConnected"] == false)
+ {
+ name = "[-] " + removeColorsFromString(name);
+ color = "^9";
+ }
+
if (team == "allies" || team == "axis")
{
// Add line with player stats
- stats = player maps\mp\gametypes\_player_stat::getStats();
- if (isDefined(stats))
+ score = " -";
+ assists = "-";
+ damage = " -";
+ plants = "-";
+ defuses = "-";
+ grenades = "-";
+ if (self.sessionteam == team || self.sessionteam == "spectator" || game["state"] == "intermission") // dont show enemy's info (it might say location of enemy)
{
- damage = int(stats["damage"] / 10) / 10;
- plants = "-";
- defuses = "-";
- if (self.sessionteam == team || self.sessionteam == "spectator" || game["state"] == "intermission") // dont show enemy's plants
- {
- plants = stats["plants"];
- defuses = stats["defuses"];
- }
+ score = format_fractional(stats["score"], 1, 1);
+ assists = stats["assists"];
- score = int(stats["score"] * 10) / 10;
+ damage = "";
+ if (stats["damage"] >= 500) damage = "^3"; // yellow
+ if (stats["damage"] >= 1000) damage = "^1"; // red
+ damage += format_fractional(stats["damage"] / 100, 1, 1);
- addLine(player, player.name, score, stats["kills"], stats["deaths"], stats["assists"], damage, plants, defuses);
-
- // Debug
- //addLine(player.name, 48, 32, 18, 4, 3.7, 2, 0);
- }
- else
- {
- addLine(player, player.name, player.score, "^1?", player.deaths, "^1?", "^1?", "^1?", "^1?");
+ plants = stats["plants"];
+ defuses = stats["defuses"];
+ grenades = stats["grenades"];
}
+
+
+
+ addLine(stats, color + name, color + score, color + stats["kills"], color + stats["deaths"], color + assists, color + damage, color + grenades, color + plants, color + defuses);
+
+ // Debug
+ //addLine(player.name, 48, 32, 18, 4, 3.7, 2, 0);
}
else
{
- addLine(player, player.name, "", "", "", "", "", "", "");
+ addLine(stats, name, "", "", "", "", "", "", "", "");
}
}
@@ -302,213 +319,256 @@ addPlayerLine(team, player)
generatePlayerList()
{
- self endon("disconnect");
+ self endon("disconnect");
+
+ // Make sure onlny 1 thread is running
+ self notify("matchinfo_lock");
+ self endon("matchinfo_lock");
- // Make sure onlny 1 thread is running
- self notify("matchinfo_lock");
- self endon("matchinfo_lock");
+ waittillframeend;
+ self setClientCvarIfChanged("ui_scoreboard_map", maps\mp\gametypes\_matchinfo::GetMapName(level.mapname));
+
+ for (;;)
+ {
+ teamplayers = getTeamPlayersArraySorted();
+
+ /*
+ type
+ 0 = nothink
+ 1 = player line
+ 2 = player line highlighted
+ 3 = team heading
+ */
+
+ // Temp variables
+ self.scoreboard = spawnstruct();
+ self.scoreboard.i = 1;
+ self.scoreboard.names = "";
+ self.scoreboard.scores = "";
+ self.scoreboard.kills = "";
+ self.scoreboard.deaths = "";
+ self.scoreboard.assists = "";
+ self.scoreboard.damages = "";
+ self.scoreboard.grenades = "";
+ self.scoreboard.plants = "";
+ self.scoreboard.defuses = "";
+
+ self.pers["scoreboard_lines_statIds"] = [];
+
+
+ teamname_allies = "";
+ teamname_axis = "";
+ if (game["scr_matchinfo"] == 1 || game["scr_matchinfo"] == 2)
+ {
+ teamname_allies = game["match_team1_name"];
+ teamname_axis = game["match_team2_name"];
+ if (game["match_team1_side"] == "axis")
+ {
+ teamname_allies = game["match_team2_name"];
+ teamname_axis = game["match_team1_name"];
+ }
+ }
- self setClientCvarIfChanged("ui_scoreboard_map", maps\mp\gametypes\_matchinfo::GetMapName(level.mapname));
+ // Generate team names
+ if (!isDefined(game["allies_score"]))
+ teamname["allies"] = "^8Allies";
+ else if (teamname_allies != "")
+ teamname["allies"] = "^8" + teamname_allies+" ("+game["allies_score"]+")";
+ else
+ teamname["allies"] = "^8Allies ("+game["allies_score"]+")";
+ if (!isDefined(game["axis_score"]))
+ teamname["axis"] = "^9Axis";
+ else if (teamname_axis != "")
+ teamname["axis"] = "^9" + teamname_axis+" ("+game["axis_score"]+")";
+ else
+ teamname["axis"] = "^9Axis ("+game["axis_score"]+")";
- for (;;)
+ // Swap teams according to score
+ team1 = "allies";
+ team2 = "axis";
+ if (game["axis_score"] > game["allies_score"])
{
- teamplayers = sortTeamsByScore();
-
- /*
- type
- 0 = nothink
- 1 = player line
- 2 = player line highlighted
- 3 = team heading
- */
-
- // Temp variables
- self.scoreboard = spawnstruct();
- self.scoreboard.i = 1;
- self.scoreboard.names = "";
- self.scoreboard.scores = "";
- self.scoreboard.kills = "";
- self.scoreboard.deaths = "";
- self.scoreboard.assists = "";
- self.scoreboard.damages = "";
- self.scoreboard.plants = "";
- self.scoreboard.defuses = "";
- self.scoreboard.pers["antilagTimeOffset"] = "";
-
- self.pers["scoreboard_lines_players"] = [];
-
-
- teamname_allies = "";
- teamname_axis = "";
- if (game["scr_matchinfo"] == 1 || game["scr_matchinfo"] == 2)
- {
- teamname_allies = game["match_team1_name"];
- teamname_axis = game["match_team2_name"];
- if (game["match_team1_side"] == "axis")
- {
- teamname_allies = game["match_team2_name"];
- teamname_axis = game["match_team1_name"];
- }
- }
+ team1 = "axis";
+ team2 = "allies";
+ }
- // Generate team names
- if (!isDefined(game["allies_score"]))
- teamname["allies"] = "^8Allies";
- else if (teamname_allies != "")
- teamname["allies"] = "^8" + teamname_allies+" ("+game["allies_score"]+")";
- else
- teamname["allies"] = "^8Allies ("+game["allies_score"]+")";
+ // Fill allies and axis team
+ addTeamLine(teamname[team1]);
+ for(p = teamplayers[team1].size-1; p >= 0; p--)
+ {
+ self addPlayerLine(team1, teamplayers[team1][p]);
+ }
+ addTeamLine(teamname[team2]);
+ for(p = teamplayers[team2].size-1; p >= 0; p--)
+ {
+ self addPlayerLine(team2, teamplayers[team2][p]);
+ }
- if (!isDefined(game["axis_score"]))
- teamname["axis"] = "^9Axis";
- else if (teamname_axis != "")
- teamname["axis"] = "^9" + teamname_axis+" ("+game["axis_score"]+")";
- else
- teamname["axis"] = "^9Axis ("+game["axis_score"]+")";
+ // Spectator
+ if (teamplayers["spectator"].size > 0)
+ {
+ addTeamLine("Spectators");
+ for(p = teamplayers["spectator"].size-1; p >= 0; p--)
+ {
+ self addPlayerLine("spectator", teamplayers["spectator"][p]);
+ }
+ }
- // Swap teams according to score
- team1 = "allies";
- team2 = "axis";
- if (game["axis_score"] > game["allies_score"])
- {
- team1 = "axis";
- team2 = "allies";
- }
+ // None
+ if (teamplayers["none"].size > 0)
+ {
+ addTeamLine("None team");
+ for(p = teamplayers["none"].size-1; p >= 0; p--)
+ {
+ self addPlayerLine("none", teamplayers["none"][p]);
+ }
+ }
- // Fill allies and axis team
- addTeamLine(teamname[team1]);
- for(p = teamplayers[team1].size-1; p >= 0; p--)
+ // Text separed by new lines
+ self setClientCvarIfChanged("ui_scoreboard_names", self.scoreboard.names);
+ self setClientCvarIfChanged("ui_scoreboard_scores", self.scoreboard.scores);
+ self setClientCvarIfChanged("ui_scoreboard_kills", self.scoreboard.kills);
+ self setClientCvarIfChanged("ui_scoreboard_assists", self.scoreboard.assists);
+ self setClientCvarIfChanged("ui_scoreboard_damages", self.scoreboard.damages);
+ self setClientCvarIfChanged("ui_scoreboard_deaths", self.scoreboard.deaths);
+ self setClientCvarIfChanged("ui_scoreboard_grenades", self.scoreboard.grenades);
+ self setClientCvarIfChanged("ui_scoreboard_plants", self.scoreboard.plants);
+ self setClientCvarIfChanged("ui_scoreboard_defuses", self.scoreboard.defuses);
+ self setClientCvarIfChanged("ui_scoreboard_ping", "");
+
+
+
+
+ // Find top player
+ topplayer = "";
+ topplanter = "";
+ topgrenader = "";
+ if (game["state"] == "intermission")
+ {
+ if (teamplayers[team1].size > 0 && teamplayers[team2].size > 0)
+ {
+ // Get top player
+ team1player = teamplayers[team1][ teamplayers[team1].size-1 ];
+ team2player = teamplayers[team2][ teamplayers[team2].size-1 ];
+
+ if (team1player["score"] >= team2player["score"])
{
- self addPlayerLine(team1, teamplayers[team1][p]);
+ topplayer = team1player["name"];
+ if (isPlayer(team1player["player"]))
+ topplayer = team1player["player"].name; // get actual player name with colors
}
- addTeamLine(teamname[team2]);
- for(p = teamplayers[team2].size-1; p >= 0; p--)
+ else
{
- self addPlayerLine(team2, teamplayers[team2][p]);
+ topplayer = team2player["name"];
+ if (isPlayer(team2player["player"]))
+ topplayer = team2player["player"].name; // get actual player name with colors
}
- // Spectator
- if (teamplayers["spectator"].size > 0)
+ // Find top planter and grenader in team 1
+ team1PlantScore = 0;
+ team1PlantPlayer = "";
+ team1GrenadeScore = 0;
+ team1GrenadePlayer = "";
+ for(p = teamplayers[team1].size-1; p >= 0; p--)
{
- addTeamLine("Spectators");
- for(p = teamplayers["spectator"].size-1; p >= 0; p--)
+ stats = teamplayers[team1][p];
+ name = stats["name"];
+ if (isPlayer(stats["player"])) name = stats["player"].name; // get actual player name with colors
+
+ plantScore = stats["plants"] + stats["defuses"];
+ if (plantScore > team1PlantScore)
{
- self addPlayerLine("spectator", teamplayers["spectator"][p]);
+ team1PlantScore = plantScore;
+ team1PlantPlayer = name;
+ }
+ if (stats["grenades"] > team1GrenadeScore)
+ {
+ team1GrenadeScore = stats["grenades"];
+ team1GrenadePlayer = name;
}
}
- // None
- if (teamplayers["none"].size > 0)
+ // Find top planter and grenader in team 2
+ team2PlantScore = 0;
+ team2PlantPlayer = "";
+ team2GrenadeScore = 0;
+ team2GrenadePlayer = "";
+ for(p = teamplayers[team2].size-1; p >= 0; p--)
{
- addTeamLine("None team");
- for(p = teamplayers["none"].size-1; p >= 0; p--)
+ stats = teamplayers[team2][p];
+ name = stats["name"];
+ if (isPlayer(stats["player"])) name = stats["player"].name; // get actual player name with colors
+
+ plantScore = stats["plants"] + stats["defuses"];
+ if (plantScore > team2PlantScore)
{
- self addPlayerLine("none", teamplayers["none"][p]);
+ team2PlantScore = plantScore;
+ team2PlantPlayer = name;
+ }
+ if (stats["grenades"] > team2GrenadeScore)
+ {
+ team2GrenadeScore = stats["grenades"];
+ team2GrenadePlayer = name;
}
}
- // Text separed by new lines
- self setClientCvarIfChanged("ui_scoreboard_names", self.scoreboard.names);
- self setClientCvarIfChanged("ui_scoreboard_scores", self.scoreboard.scores);
- self setClientCvarIfChanged("ui_scoreboard_kills", self.scoreboard.kills);
- self setClientCvarIfChanged("ui_scoreboard_assists", self.scoreboard.assists);
- self setClientCvarIfChanged("ui_scoreboard_damages", self.scoreboard.damages);
- self setClientCvarIfChanged("ui_scoreboard_deaths", self.scoreboard.deaths);
- self setClientCvarIfChanged("ui_scoreboard_plants", self.scoreboard.plants);
- self setClientCvarIfChanged("ui_scoreboard_defuses", self.scoreboard.defuses);
- self setClientCvarIfChanged("ui_scoreboard_ping", ""/*self.scoreboard.pers["antilagTimeOffset"]*/);
-
-
-
+ if (team1PlantScore >= 4 || team2PlantScore >= 4)
+ {
+ if (team2PlantScore > team1PlantScore) topplanter = team2PlantPlayer;
+ else topplanter = team1PlantPlayer;
+ }
- // Find top player
- topplayer = "";
- topplanter = "";
- if (!game["readyup_first_run"])
+ if (team1GrenadeScore >= 4 || team2GrenadeScore >= 4)
{
- if (teamplayers[team1].size > 0 && teamplayers[team2].size > 0)
- {
- team1player = teamplayers[team1][ teamplayers[team1].size-1 ];
- team2player = teamplayers[team2][ teamplayers[team2].size-1 ];
-
- if (team1player.stats["score"] >= team2player.stats["score"])
- topplayer = team1player.name;
- else
- topplayer = team2player.name;
-
- // Find top planter team 1
- team1PlantScore = 0;
- team1PlantPlayer = "";
- for(p = teamplayers[team1].size-1; p >= 0; p--)
- {
- player = teamplayers[team1][p];
-
- if (isDefined(player.stats))
- {
- plantScore = player.stats["plants"] + player.stats["defuses"];
-
- if (plantScore > team1PlantScore)
- {
- team1PlantScore = plantScore;
- team1PlantPlayer = player.name;
- }
- }
- }
-
- // Find top planter team 2
- team2PlantScore = 0;
- team2PlantPlayer = "";
- for(p = teamplayers[team2].size-1; p >= 0; p--)
- {
- player = teamplayers[team2][p];
-
- if (isDefined(player.stats))
- {
- plantScore = player.stats["plants"] + player.stats["defuses"];
-
- if (plantScore > team2PlantScore)
- {
- team2PlantScore = plantScore;
- team2PlantPlayer = player.name;
- }
- }
- }
-
- if (team2PlantScore > team1PlantScore)
- topplanter = team2PlantPlayer;
- else
- topplanter = team1PlantPlayer;
- }
+ if (team2GrenadeScore > team1GrenadeScore) topgrenader = team2GrenadePlayer;
+ else topgrenader = team1GrenadePlayer;
}
+ }
+ }
- toptext = "";
- if (topplayer != "")
- toptext = "^9Top player: ^7" + topplayer + "\n^7";
- if (topplanter != "")
- toptext += "^9Bomb expert: ^7" + topplanter;
+ toptext = "";
+ if (topplayer != "")
+ toptext = "^9Top player: ^7" + topplayer + "\n^7";
+ if (topplanter != "")
+ toptext += "^9Bomb expert: ^7" + topplanter + "\n^7";
+ if (topgrenader != "")
+ toptext += "^9Grenade expert: ^7" + topgrenader;
+ addEmptyLine();
+ addEmptyLine();
+ addTeamLine(toptext);
- addEmptyLine();
- addEmptyLine();
- addTeamLine(toptext);
+ // Fill empty lines
+ for(; self.scoreboard.i <= 26; self.scoreboard.i++)
+ {
+ self setClientCvarIfChanged("ui_scoreboard_line_"+self.scoreboard.i, "0");
+ }
- // Fill empty lines
- for(; self.scoreboard.i <= 24; self.scoreboard.i++)
- {
- self setClientCvarIfChanged("ui_scoreboard_line_"+self.scoreboard.i, "0");
- }
+ i = 0;
+ while(1)
+ {
+ wait level.frame;
+ i++;
- // Keep updating untill we start moving in readyup
- if (self.isMoving || !level.in_readyup)
- break;
+ // 1 sec elapsed
+ if (i > level.sv_fps * 1 || self attackbuttonpressed())
+ {
+ if ((self.sessionstate == "playing" && self.isMoving) || self attackbuttonpressed())
+ {
+ self.pers["scoreboard_keepRefreshing"] = false; // to refresh in next round
+ return;
+ }
else
- wait level.fps_multiplier * 1;
-
- //self iprintln("updating scoreboard...");
+ self.pers["scoreboard_keepRefreshing"] = true;
+ break;
+ }
}
+
+ //self iprintln("updating scoreboard...");
+ }
}
diff --git a/source/maps/mp/gametypes/_menu_serverinfo.gsc b/source/maps/mp/gametypes/_menu_serverinfo.gsc
index e8d5ba7..461d9f1 100644
--- a/source/maps/mp/gametypes/_menu_serverinfo.gsc
+++ b/source/maps/mp/gametypes/_menu_serverinfo.gsc
@@ -5,9 +5,10 @@ init()
addEventListener("onStartGameType", ::onStartGameType);
addEventListener("onCvarChanged", ::onCvarChanged);
- registerCvar("scr_motd", "STRING", "Welcome. This server is running zPAM3.31"); // ZPAM_RENAME
+ registerCvarEx("I", "scr_motd", "STRING", "Welcome. This server is running zPAM3.32"); // ZPAM_RENAME
level.motd = "";
+ level.serverversion = "";
level.serverinfo_left1 = "";
level.serverinfo_left2 = "";
level.serverinfo_right1 = "";
@@ -45,6 +46,7 @@ onCvarChanged(cvar, value, isRegisterTime)
updateServerInfo()
{
self setClientCvarIfChanged("ui_motd", level.motd);
+ self setClientCvarIfChanged("ui_serverversion", level.serverversion);
// These are set from gametype
self setClientCvarIfChanged("ui_serverinfo_left1", level.serverinfo_left1);
@@ -84,6 +86,25 @@ generateGlobalServerInfo()
+ level.serverversion = getCvar("version") + " "; // "CoD2 MP 1.3 build pc_1.3_1_1 Mon May 01 2006 05:05:43PM win-x86"
+
+ sys_cpuGHz = getCvarFloat("sys_cpuGHz"); // "2.59199"
+ if (sys_cpuGHz != 0)
+ level.serverversion += " (CPU " + format_fractional(sys_cpuGHz, 1, 2) + " GHz)";
+
+ sys_gpu = getcvar("sys_gpu"); // "Intel(R) UHD Graphics"
+ if (sys_gpu != "")
+ level.serverversion += " (GPU " + sys_gpu + ")";
+
+ level.serverversion += " (" + getcvarint("sv_maxclients") + " slots)"; // (14 slots)
+
+ if (contains(level.serverversion, "win"))
+ level.serverversion += " (Windows server)";
+ else if (contains(level.serverversion, "linux"))
+ level.serverversion += " (Linux server)";
+
+
+
title = "PAM mode:\n";
value = level.pam_mode + "\n";
diff --git a/source/maps/mp/gametypes/_menus.gsc b/source/maps/mp/gametypes/_menus.gsc
index 9dbf375..7db8600 100644
--- a/source/maps/mp/gametypes/_menus.gsc
+++ b/source/maps/mp/gametypes/_menus.gsc
@@ -16,6 +16,7 @@ onStartGameType()
if (!isDefined(game["menuPrecached"]))
{
game["menu_moddownload"] = "moddownload";
+ game["menu_language"] = "language";
game["menu_ingame"] = "ingame";
game["menu_team"] = "team_" + game["allies"] + game["axis"]; // team_britishgerman
game["menu_weapon_allies"] = "weapon_" + game["allies"];
@@ -34,6 +35,7 @@ onStartGameType()
game["menu_strat_records"] = "strat_records";
precacheMenu(game["menu_moddownload"]);
+ precacheMenu(game["menu_language"]);
precacheMenu(game["menu_ingame"]);
precacheMenu(game["menu_team"]);
precacheMenu(game["menu_weapon_allies"]);
@@ -92,24 +94,34 @@ onConnected()
// dont open any menu
}
- // If player is just connected
- else if (self.pers["team"] == "none")
+ // Server info wasnt skiped yet - player didnt click "continue"
+ else if(!isDefined(self.pers["skipserverinfo"]))
+ {
+ scriptMainMenu = game["menu_serverinfo"];
+ self openMenu(game["menu_serverinfo"]);
+ self maps\mp\gametypes\_menu_serverinfo::updateServerInfo();
+ }
+
+ // Menu with language was not opened yet
+ else if (!isDefined(self.pers["languageLoaded"]))
+ {
+ scriptMainMenu = game["menu_language"];
+ self openMenu(game["menu_language"]);
+ }
+
+ else if (game["state"] == "intermission")
{
self setClientCvar2("ui_allow_weaponchange", 0);
+ self setClientCvar2("ui_allow_changeteam", 0);
+ scriptMainMenu = game["menu_ingame"]; // default
+ }
- // Server info wasnt skiped yet - player didnt click "continue"
- if(!isDefined(self.pers["skipserverinfo"]))
- {
- scriptMainMenu = game["menu_serverinfo"];
- self openMenu(game["menu_serverinfo"]);
- self maps\mp\gametypes\_menu_serverinfo::updateServerInfo();
- }
- // Server info is skipped
- else
- {
- scriptMainMenu = game["menu_team"]; // when esc is pressed, show menu with teams
- self openMenu(game["menu_team"]); // else open team menu
- }
+ // Player did not choose team yet after connect
+ else if (!isDefined(self.pers["firstTeamSelected"]))
+ {
+ self setClientCvar2("ui_allow_changeteam", 1);
+ scriptMainMenu = game["menu_team"]; // when esc is pressed, show menu with teams
+ self openMenu(game["menu_team"]); // else open team menu
}
// If team is selected
@@ -149,6 +161,9 @@ onConnected()
onJoinedTeam(team)
{
+ self.pers["firstTeamSelected"] = true;
+
+
if (team == "allies" || team == "axis")
{
self setClientCvar2("ui_allow_weaponchange", "1");
@@ -195,18 +210,59 @@ onMenuResponse(menu, response)
{
maps\mp\gametypes\_force_download::modIsDownloaded();
- self setClientCvar2("g_scriptMainMenu", game["menu_serverinfo"]);
+ /*
+ TODO LANG
+ // Open language menu
+ self closeMenu();
+ self closeInGameMenu();
+ self openMenu(game["menu_language"]);
+ self setClientCvar2("g_scriptMainMenu", game["menu_language"]);
+
+ return true;
+ }
+
+ else if (menu == game["menu_language"])
+ {
+ maps\mp\gametypes\_language::saveOriginalLanguage(response);
+ TODO LANG
+ */
+
+ self.pers["languageLoaded"] = true;
// Open server info
self closeMenu();
self closeInGameMenu();
self openMenu(game["menu_serverinfo"]);
+ self setClientCvar2("g_scriptMainMenu", game["menu_serverinfo"]);
self maps\mp\gametypes\_menu_serverinfo::updateServerInfo();
+ }
+
+ else if(menu == game["menu_serverinfo"] && response == "close")
+ {
+ self closeMenu();
+ self closeInGameMenu();
+
+ if (game["state"] != "intermission")
+ self setClientCvar2("ui_allow_changeteam", 1);
+
+ if (!isDefined(self.pers["skipserverinfo"])) // first time
+ {
+ // After serverinfo is skipped, show menu with teams
+ self setClientCvar2("g_scriptMainMenu", game["menu_team"]);
+ self openMenu(game["menu_team"]);
+ }
+ else if (!isDefined(self.pers["firstTeamSelected"]))
+ self openMenu(game["menu_team"]);
+ else
+ self openMenu(game["menu_ingame"]); // serverinfo may be opened and closed aditionally via menu
+
+ self.pers["skipserverinfo"] = true;
return true;
}
+
if(response == "back")
{
self closeMenu();
@@ -223,7 +279,7 @@ onMenuResponse(menu, response)
if(menu == game["menu_ingame"])
{
- if (response == "changeweapon")
+ if (response == "changeweapon" && game["state"] != "intermission")
{
self closeMenu();
self closeInGameMenu();
@@ -233,7 +289,7 @@ onMenuResponse(menu, response)
self openMenu(game["menu_weapon_axis"]);
return true;
}
- if (response == "changeteam")
+ if (response == "changeteam" && game["state"] != "intermission")
{
self closeMenu();
self closeInGameMenu();
@@ -257,7 +313,7 @@ onMenuResponse(menu, response)
}
}
- else if(menu == game["menu_team"])
+ else if(menu == game["menu_team"] && game["state"] != "intermission")
{
teamBefore = self.pers["team"];
switch(response)
@@ -328,26 +384,6 @@ onMenuResponse(menu, response)
maps\mp\gametypes\_quickmessages::quickresponses(response);
return true;
}
- else if(menu == game["menu_serverinfo"] && response == "close")
- {
- self closeMenu();
- self closeInGameMenu();
-
- if (!isDefined(self.pers["skipserverinfo"])) // first time
- {
- // After serverinfo is skipped, show menu with teams
- self setClientCvar2("g_scriptMainMenu", game["menu_team"]);
- self openMenu(game["menu_team"]);
- }
- else if (self.pers["team"] == "none")
- self openMenu(game["menu_team"]);
- else
- self openMenu(game["menu_ingame"]);
-
- self.pers["skipserverinfo"] = true;
-
- return true;
- }
}
diff --git a/source/maps/mp/gametypes/_pam.gsc b/source/maps/mp/gametypes/_pam.gsc
index 7586a3b..1026728 100644
--- a/source/maps/mp/gametypes/_pam.gsc
+++ b/source/maps/mp/gametypes/_pam.gsc
@@ -4,8 +4,8 @@ init()
{
if(game["firstInit"])
{
- precacheString2("STRING_VERSION_INFO", &"zPAM 3.31 ^3PREVIEW"); // ZPAM_RENAME
- //precacheString2("STRING_VERSION_INFO", &"^1zPAM 3.31 TEST 1"); // ZPAM_RENAME
+ precacheString2("STRING_VERSION_INFO", &"zPAM 3.32 ^3PREVIEW"); // ZPAM_RENAME
+ //precacheString2("STRING_VERSION_INFO", &"^1zPAM 3.32 TEST 2"); // ZPAM_RENAME
}
}
diff --git a/source/maps/mp/gametypes/_player_stat.gsc b/source/maps/mp/gametypes/_player_stat.gsc
index 4141fe1..635872f 100644
--- a/source/maps/mp/gametypes/_player_stat.gsc
+++ b/source/maps/mp/gametypes/_player_stat.gsc
@@ -13,6 +13,7 @@ init()
addEventListener("onConnected", ::onConnected);
addEventListener("onDisconnect", ::onDisconnect);
addEventListener("onConnectedAll", ::onConnectedAll);
+ addEventListener("onJoinedTeam", ::onJoinedTeam);
//thread printThread();
}
@@ -147,6 +148,7 @@ print()
println("game[playerstats]["+i+"][assists] = " + data["assists"]);
println("game[playerstats]["+i+"][deaths] = " + data["deaths"]);
println("game[playerstats]["+i+"][kills] = " + data["score"]);
+ println("game[playerstats]["+i+"][grenades] = " + data["grenades"]);
println("game[playerstats]["+i+"][plants] = " + data["plants"]);
println("game[playerstats]["+i+"][defuses] = " + data["defuses"]);
}
@@ -178,55 +180,70 @@ printThread()
onConnected()
{
- handleRename();
-
- id = self getEntityNumber();
-
- dataId = findData(self.name, id);
- if (dataId >= 0 && game["playerstats"][dataId]["isConnected"] == false)
- {
- game["playerstats"][dataId]["isConnected"] = true;
- game["playerstats"][dataId]["lastTime"] = getTime();
-
- self thread restoreScore(game["playerstats"][dataId]["kills"], game["playerstats"][dataId]["deaths"]);
- }
- else
- {
- // Player was not found via name and id, give other players connecting in same time time to identified them via name and id before this player will be found only by name.. (to avoid stat steal by duplicating name)
- waittillframeend;
-
- dataId = findData(self.name);
- if (dataId >= 0 && game["playerstats"][dataId]["isConnected"] == false)
- {
- game["playerstats"][dataId]["entityId"] = id;
- game["playerstats"][dataId]["isConnected"] = true;
- game["playerstats"][dataId]["lastTime"] = getTime();
-
- self thread restoreScore(game["playerstats"][dataId]["kills"], game["playerstats"][dataId]["deaths"]);
- //setCvar("sv_playerstats_" + dataId + "_entityId", id);
- }
- else
- {
- // Create new entry
- newIndex = game["playerstats"].size;
- game["playerstats"][newIndex]["deleted"] = false;
- game["playerstats"][newIndex]["entityId"] = id;
- game["playerstats"][newIndex]["name"] = self.name;
- game["playerstats"][newIndex]["isConnected"] = true;
- game["playerstats"][newIndex]["lastTime"] = getTime();
- game["playerstats"][newIndex]["kills"] = 0;
- game["playerstats"][newIndex]["assists"] = 0;
- game["playerstats"][newIndex]["damage"] = 0;
- game["playerstats"][newIndex]["deaths"] = 0;
- game["playerstats"][newIndex]["score"] = 0.0;
- game["playerstats"][newIndex]["plants"] = 0;
- game["playerstats"][newIndex]["defuses"] = 0;
- }
- }
+ handleRename();
+
+ id = self getEntityNumber();
+
+ dataId = findData(self.name, id);
+ if (dataId >= 0 && game["playerstats"][dataId]["isConnected"] == false) // note: isConnected is reseted every map_restart
+ {
+ game["playerstats"][dataId]["player"] = self;
+ game["playerstats"][dataId]["isConnected"] = true;
+ game["playerstats"][dataId]["team"] = self.sessionteam;
+ game["playerstats"][dataId]["lastTime"] = getTime();
+
+ self thread restoreScore(game["playerstats"][dataId]["kills"], game["playerstats"][dataId]["deaths"]);
+ }
+ else
+ {
+ // Player was not found via name and id, give other players connecting in same time time to identified them via name and id before this player will be found only by name.. (to avoid stat steal by duplicating name)
+ waittillframeend;
+
+ dataId = findData(self.name);
+ if (dataId >= 0 && game["playerstats"][dataId]["isConnected"] == false)
+ {
+ game["playerstats"][dataId]["player"] = self;
+ game["playerstats"][dataId]["entityId"] = id;
+ game["playerstats"][dataId]["isConnected"] = true;
+ game["playerstats"][dataId]["team"] = self.sessionteam;
+ game["playerstats"][dataId]["lastTime"] = getTime();
+
+ self thread restoreScore(game["playerstats"][dataId]["kills"], game["playerstats"][dataId]["deaths"]);
+ //setCvar("sv_playerstats_" + dataId + "_entityId", id);
+ }
+ else
+ {
+ // Create new entry
+ newIndex = game["playerstats"].size;
+ game["playerstats"][newIndex]["id"] = newIndex;
+ game["playerstats"][newIndex]["player"] = self;
+ game["playerstats"][newIndex]["deleted"] = false;
+ game["playerstats"][newIndex]["entityId"] = id;
+ game["playerstats"][newIndex]["name"] = self.name;
+ game["playerstats"][newIndex]["team"] = self.sessionteam;
+ game["playerstats"][newIndex]["isConnected"] = true;
+ game["playerstats"][newIndex]["lastTime"] = getTime();
+ game["playerstats"][newIndex]["kills"] = 0;
+ game["playerstats"][newIndex]["assists"] = 0;
+ game["playerstats"][newIndex]["damage"] = 0;
+ game["playerstats"][newIndex]["deaths"] = 0;
+ game["playerstats"][newIndex]["score"] = 0.0;
+ game["playerstats"][newIndex]["grenades"] = 0;
+ game["playerstats"][newIndex]["plants"] = 0;
+ game["playerstats"][newIndex]["defuses"] = 0;
+ }
+ }
}
-
+onJoinedTeam(team)
+{
+ dataId = findData(self.name, self getEntityNumber());
+ if (dataId >= 0)
+ {
+ game["playerstats"][dataId]["team"] = team;
+ }
+}
onDisconnect()
@@ -235,10 +252,10 @@ onDisconnect()
if (dataId >= 0)
{
game["playerstats"][dataId]["isConnected"] = false;
+ game["playerstats"][dataId]["player"] = undefined;
game["playerstats"][dataId]["lastTime"] = getTime();
+ game["playerstats"][dataId]["name"] = self.name; // in case player renamed since connect
}
-
- //level thread restartIfEmpty();
}
@@ -305,6 +322,15 @@ AddDeath()
}
}
+AddGrenade()
+{
+ dataId = self getStatId();
+ if (dataId >= 0)
+ {
+ game["playerstats"][dataId]["grenades"] += 1;
+ }
+}
+
AddPlant()
{
dataId = self getStatId();
diff --git a/source/maps/mp/gametypes/_players_left.gsc b/source/maps/mp/gametypes/_players_left.gsc
index ac9fe4b..e4709dd 100644
--- a/source/maps/mp/gametypes/_players_left.gsc
+++ b/source/maps/mp/gametypes/_players_left.gsc
@@ -51,14 +51,12 @@ onCvarChanged(cvar, value, isRegisterTime)
onConnected()
{
+ // Enable player list by default, can be overwritten by player settings
+ if (!isDefined(self.pers["playersleft_list"]))
+ self.pers["playersleft_list"] = true;
+
if (level.scr_show_players_left)
- {
self thread createHUD();
-
- // Enable player list by default, can be overwritten by player settings
- if (!isDefined(self.pers["playersleft_list"]))
- enable();
- }
else
self hide(); // hide is set from previous map
}
diff --git a/source/maps/mp/gametypes/_quicksettings.gsc b/source/maps/mp/gametypes/_quicksettings.gsc
index f125e6e..8094f36 100644
--- a/source/maps/mp/gametypes/_quicksettings.gsc
+++ b/source/maps/mp/gametypes/_quicksettings.gsc
@@ -2,21 +2,21 @@
init()
{
- addEventListener("onConnected", ::onConnected);
- addEventListener("onMenuResponse", ::onMenuResponse);
+ addEventListener("onConnected", ::onConnected);
+ addEventListener("onMenuResponse", ::onMenuResponse);
}
onConnected()
{
- self endon("disconnect");
+ self endon("disconnect");
- if (!isDefined(self.pers["quicksettings_saved"]))
- self.pers["quicksettings_saved"] = false;
+ if (!isDefined(self.pers["quicksettings_saved"]))
+ self.pers["quicksettings_saved"] = false;
- wait level.fps_multiplier * 1;
+ wait level.fps_multiplier * 1;
- if (!self.pers["quicksettings_saved"])
- self updateClientSettings(); // write defaults
+ if (!self.pers["quicksettings_saved"])
+ self updateClientSettings(); // write defaults
}
/*
@@ -28,83 +28,96 @@ onMenuResponse(menu, response)
{
if (menu == game["menu_quicksettings"])
{
- self parseString(response);
+ self parseString(response);
return true;
}
}
parseString(string)
{
- // set server16 "openscriptmenu quicksettings autorecording_0|matchinfo_0|teamscore_0|"
+ // set server16 "openscriptmenu quicksettings autorecording_0|matchinfo_0|teamscore_0|"
+ //iprintln(string);
+ // Settings are formated in format "- _|_|..."
- // Settings are formated in format "
- _|_|..."
-
- if (string.size == 0)
- {
- self updateClientSettings(); // write defaults
- return;
- }
-
- lastItemPos = 0;
- for (i = 0; i <= string.size; i++)
- {
- if (i == string.size)
- char = "|"; // used for last item
- else
- char = string[i];
-
- if (char == "|")
- {
- item = ToLower(getsubstr(string, lastItemPos, i)); //
- _
-
- if (item == "autorecording") self maps\mp\gametypes\_record::toggle();
- else if (item == "autorecording_0") self maps\mp\gametypes\_record::disable();
- else if (item == "autorecording_1") self maps\mp\gametypes\_record::enable();
-
- else if (item == "matchinfo") self maps\mp\gametypes\_matchinfo::ingame_toggle();
- else if (item == "matchinfo_0") self maps\mp\gametypes\_matchinfo::ingame_disable();
- else if (item == "matchinfo_1") self maps\mp\gametypes\_matchinfo::ingame_enable();
-
- else if (item == "score") self maps\mp\gametypes\_hud_teamscore::toggle();
- else if (item == "score_0") self maps\mp\gametypes\_hud_teamscore::disable();
- else if (item == "score_1") self maps\mp\gametypes\_hud_teamscore::enable();
-
- else if (item == "playersleft") self maps\mp\gametypes\_players_left::toggle();
- else if (item == "playersleft_0") self maps\mp\gametypes\_players_left::disable();
- else if (item == "playersleft_1") self maps\mp\gametypes\_players_left::enable();
+ if (string.size == 0)
+ {
+ self updateClientSettings(); // write defaults
+ return;
+ }
- lastItemPos = i + 1;
- }
- }
+ lastItemPos = 0;
+ for (i = 0; i <= string.size; i++)
+ {
+ if (i == string.size)
+ char = "|"; // used for last item
+ else
+ char = string[i];
+
+ if (char == "|")
+ {
+ item = ToLower(getsubstr(string, lastItemPos, i)); //
- _
+
+ if (item == "autorecording") self maps\mp\gametypes\_record::toggle();
+ else if (item == "autorecording_0") self maps\mp\gametypes\_record::disable();
+ else if (item == "autorecording_1") self maps\mp\gametypes\_record::enable();
+
+ else if (item == "matchinfo") self maps\mp\gametypes\_matchinfo::ingame_toggle();
+ else if (item == "matchinfo_0") self maps\mp\gametypes\_matchinfo::ingame_disable();
+ else if (item == "matchinfo_1") self maps\mp\gametypes\_matchinfo::ingame_enable();
+
+ else if (item == "score") self maps\mp\gametypes\_hud_teamscore::toggle();
+ else if (item == "score_0") self maps\mp\gametypes\_hud_teamscore::disable();
+ else if (item == "score_1") self maps\mp\gametypes\_hud_teamscore::enable();
+
+ else if (item == "playersleft") self maps\mp\gametypes\_players_left::toggle();
+ else if (item == "playersleft_0") self maps\mp\gametypes\_players_left::disable();
+ else if (item == "playersleft_1") self maps\mp\gametypes\_players_left::enable();
+ /* TOTO LANG
+ else if (item == "language") self maps\mp\gametypes\_language::toggle();
+ else if (item == "language_-1") self maps\mp\gametypes\_language::select(-1);
+ else if (item == "language_0") self maps\mp\gametypes\_language::select(0);
+ else if (item == "language_1") self maps\mp\gametypes\_language::select(1);
+ else if (item == "language_2") self maps\mp\gametypes\_language::select(2);
+ else if (item == "language_6") self maps\mp\gametypes\_language::select(6);
+ else if (item == "language_7") self maps\mp\gametypes\_language::select(7);
+ else if (item == "language_14") self maps\mp\gametypes\_language::select(14);
+ else if (item == "language_15") self maps\mp\gametypes\_language::select(15);
+ */
+ lastItemPos = i + 1;
+ }
+ }
- self updateClientSettings();
+ self updateClientSettings();
}
updateClientSettings()
{
- string = "openscriptmenu quicksettings ";
- delimiter = "|";
-
- recording = self maps\mp\gametypes\_record::isEnabled();
- matchinfo = self maps\mp\gametypes\_matchinfo::ingame_isEnabled();
- score = self maps\mp\gametypes\_hud_teamscore::isEnabled();
- playersleft = self maps\mp\gametypes\_players_left::isEnabled();
-
- string = string + "autorecording_" + recording;
- string = string + delimiter;
- string = string + "matchinfo_" + matchinfo;
- string = string + delimiter;
- string = string + "score_" + score;
- string = string + delimiter;
- string = string + "playersleft_" + playersleft;
-
- self setClientCvar2("server16", string);
-
- // Cvars for switching text in quick menu
- self setClientCvar2("ui_quicksettings_autorecording", recording);
- self setClientCvar2("ui_quicksettings_matchinfo", matchinfo);
- self setClientCvar2("ui_quicksettings_score", score);
- self setClientCvar2("ui_quicksettings_playersleft", playersleft);
-
- self.pers["quicksettings_saved"] = true;
+ string = "openscriptmenu quicksettings ";
+ delimiter = "|";
+
+ recording = self maps\mp\gametypes\_record::isEnabled();
+ matchinfo = self maps\mp\gametypes\_matchinfo::ingame_isEnabled();
+ score = self maps\mp\gametypes\_hud_teamscore::isEnabled();
+ playersleft = self maps\mp\gametypes\_players_left::isEnabled();
+ //language = self maps\mp\gametypes\_language::getValue(); TODO lang
+
+ string = string + "autorecording_" + recording;
+ string = string + delimiter;
+ string = string + "matchinfo_" + matchinfo;
+ string = string + delimiter;
+ string = string + "score_" + score;
+ string = string + delimiter;
+ string = string + "playersleft_" + playersleft;/*
+ string = string + delimiter; TODO lang
+ string = string + "language_" + language;*/
+
+ self setClientCvar2("server16", string);
+
+ // Cvars for switching text in quick menu
+ self setClientCvar2("ui_quicksettings_autorecording", recording);
+ self setClientCvar2("ui_quicksettings_matchinfo", matchinfo);
+ self setClientCvar2("ui_quicksettings_score", score);
+ self setClientCvar2("ui_quicksettings_playersleft", playersleft);
+
+ self.pers["quicksettings_saved"] = true;
}
diff --git a/source/maps/mp/gametypes/_readyup.gsc b/source/maps/mp/gametypes/_readyup.gsc
index 1693b36..bf46da8 100644
--- a/source/maps/mp/gametypes/_readyup.gsc
+++ b/source/maps/mp/gametypes/_readyup.gsc
@@ -154,6 +154,10 @@ Start_Readyup_Mode(runned_in_middle_of_game)
}
}
+ waittillframeend; // wait until timeout, overtime etc.. init() func are called
+
+ level.playersready = false;
+
// Create "Ready-up Mode", "Waiting On X Players", "Clock", "Waring: PB is off.." atd...
createLevelHUD();
@@ -293,6 +297,9 @@ onPlayerDamaging(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon
if (self.flying)
return true;
+ if (sMeansOfDeath == "MOD_GRENADE_SPLASH")
+ return true;
+
if (isPlayer(eAttacker) && self != eAttacker)
{
if (eAttacker.flying)
@@ -429,7 +436,7 @@ playerReadyUpThread()
}
// Dont show readyup for just connected players
- while(self.pers["team"] == "none")
+ while(!isDefined(self.pers["firstTeamSelected"]))
wait level.fps_multiplier * 0.1;
// Dont show readyup if player is in team but weapon is not selected yet (player is in spectator session state)
@@ -460,7 +467,7 @@ playerReadyUpThread()
unsetReady();
- // Check if all players are ready
+ // Check if all players are ready
level thread Check_All_Ready();
@@ -473,7 +480,7 @@ playerReadyUpThread()
else if (self MeleeButtonPressed() && !level.in_timeout && (self.pers["team"] == "allies" || self.pers["team"] == "axis"))
{
catch_next = false;
- for(i=0; i<=10; i++)
+ for(i = 0; i <= level.sv_fps * 0.5; i++)
{
if(catch_next && self meleeButtonPressed())
{
@@ -488,12 +495,12 @@ playerReadyUpThread()
else if(!(self meleeButtonPressed()))
catch_next = true;
- wait level.fps_multiplier * 0.01;
+ wait level.frame;
}
}
else
- wait level.fps_multiplier * 0.1;
+ wait level.frame;
}
@@ -594,7 +601,7 @@ areAllPlayersReady()
PrintTeamAndHowToUse()
{
- if (self.pers["team"] == "none")
+ if (!isDefined(self.pers["firstTeamSelected"]))
return;
// Spawned as spectator when chosing a weapon, wait untill spawned as player or as full spectator
@@ -667,8 +674,6 @@ End_Readyup_Mode()
wait level.fps_multiplier * level.scr_readyup_start_timer; // (10sec)
- level.in_readyup = 0;
-
// reset flag as readyup was runned
game["readyup_first_run"] = false;
@@ -677,6 +682,9 @@ End_Readyup_Mode()
if (!level.pam_mode_change)
map_restart(true);
}
+ else
+ // Reset only in timeout because normally it will get reseted by map_restart and also this caused to add 1 death to players if in the same frame player was killed
+ level.in_readyup = 0;
}
@@ -807,8 +815,6 @@ Update_Players_Count()
createLevelHUD()
{
- level.playersready = false;
-
// Show name of league + pam version
thread maps\mp\gametypes\_pam::PAM_Header();
@@ -929,7 +935,7 @@ HUD_Clock()
}
-// Ready-Uo Mode / Halft time Ready-Up Mode / Timeout Ready-Up Mode
+// Ready-Up Mode / Halft time Ready-Up Mode / Timeout Ready-Up Mode
HUD_ReadyUpMode()
{
level.readyup_mode = newHudElem2();
@@ -1141,7 +1147,7 @@ HUD_ReadyUp_ResumingIn_Delete()
// Called each time player is spawned
HUD_SelectTeam()
{
- if (self.pers["team"] == "none")
+ if (!isDefined(self.pers["firstTeamSelected"]))
{
if (!isDefined(self.selectTeamInfoBG))
{
diff --git a/source/maps/mp/gametypes/_record.gsc b/source/maps/mp/gametypes/_record.gsc
index 8e3cf91..fd8817e 100644
--- a/source/maps/mp/gametypes/_record.gsc
+++ b/source/maps/mp/gametypes/_record.gsc
@@ -18,9 +18,6 @@ init()
if (!level.scr_recording)
return;
- if (game["scr_matchinfo"] == 0) // used to get team names
- return;
-
// Match ID is just random string to avoid demo overwrite
if (!isDefined(game["recordingMatchID"]))
game["recordingMatchID"] = generateHash(2);
@@ -113,7 +110,7 @@ onSpawned()
if (!self.pers["recording_executed"])
{
// If player connect in the middle of the match, start recording (recording was not runned yet)
- if (!level.in_readyup && !level.in_timeout && (self.sessionteam == "allies" || self.sessionteam == "axis"))
+ if (!level.in_readyup && !level.in_timeout && (self.pers["team"] == "allies" || self.pers["team"] == "axis"))
{
waittillframeend; // wait until team names are generated
self execRecording();
@@ -192,83 +189,96 @@ generateDemoName()
// Wait untill team names are generated in case recording is executed right at start of new round
waittillframeend;
- // Assing my team name as first
- myTeamName = game["match_team1_name"];
- enemyTeamName = game["match_team2_name"];
- if (self.pers["team"] == game["match_team2_side"])
- {
- myTeamName = game["match_team2_name"];
- enemyTeamName = game["match_team1_name"];
- }
-
+ teamPrefix = "";
- // Protection if team names from match info are empty
- if (myTeamName == "" || enemyTeamName == "")
+ // If team names are turned off (deathmatch)
+ if (game["scr_matchinfo"] > 0)
{
- maps\mp\gametypes\_teamname::refreshTeamName("allies"); // will update level.teamname_allies
- maps\mp\gametypes\_teamname::refreshTeamName("axis"); // will update level.teamname_axis
-
- if (self.pers["team"] == "allies")
+ // Assing my team name as first
+ myTeamName = game["match_team1_name"];
+ enemyTeamName = game["match_team2_name"];
+ if (self.pers["team"] == game["match_team2_side"])
{
- myTeamName = level.teamname_allies;
- enemyTeamName = level.teamname_axis;
+ myTeamName = game["match_team2_name"];
+ enemyTeamName = game["match_team1_name"];
}
- else
+
+
+ // Protection if team names from match info are empty
+ if (myTeamName == "" || enemyTeamName == "")
{
- myTeamName = level.teamname_axis;
- enemyTeamName = level.teamname_allies;
- }
- }
+ maps\mp\gametypes\_teamname::refreshTeamName("allies"); // will update level.teamname_allies
+ maps\mp\gametypes\_teamname::refreshTeamName("axis"); // will update level.teamname_axis
+ if (self.pers["team"] == "allies")
+ {
+ myTeamName = level.teamname_allies;
+ enemyTeamName = level.teamname_axis;
+ }
+ else
+ {
+ myTeamName = level.teamname_axis;
+ enemyTeamName = level.teamname_allies;
+ }
+ }
- // Generated name is empty
- if (myTeamName == "")
- myTeamName = "!";
- if (enemyTeamName == "")
- enemyTeamName = "!";
+ // Generated name is empty
+ if (myTeamName == "")
+ myTeamName = "!";
+ if (enemyTeamName == "")
+ enemyTeamName = "!";
- // Get only a-z A-Z 0-9
- myTeamName = getSecureString(myTeamName);
- enemyTeamName = getSecureString(enemyTeamName);
- // Limit length
- enemyExtraLen = 11 - enemyTeamName.size;
- if (enemyExtraLen < 0) enemyExtraLen = 0;
- myExtraLen = 7 - myTeamName.size;
- if (myExtraLen < 0) myExtraLen = 0;
+ // Get only a-z A-Z 0-9
+ myTeamName = getSecureString(myTeamName);
+ enemyTeamName = getSecureString(enemyTeamName);
- myTeamName = getsubstr(myTeamName, 0, 7 + enemyExtraLen);
- enemyTeamName = getsubstr(enemyTeamName, 0, 11 + myExtraLen);
+ // Limit length
+ enemyExtraLen = 11 - enemyTeamName.size;
+ if (enemyExtraLen < 0) enemyExtraLen = 0;
+ myExtraLen = 7 - myTeamName.size;
+ if (myExtraLen < 0) myExtraLen = 0;
- // Get number of players
- myTeamPlayers = 0;
- enemyTeamPlayers = 0;
- players = getentarray("player", "classname");
- for(i = 0; i < players.size; i++)
- {
- player = players[i];
+ myTeamName = getsubstr(myTeamName, 0, 7 + enemyExtraLen);
+ enemyTeamName = getsubstr(enemyTeamName, 0, 11 + myExtraLen);
- if (player.pers["team"] == "allies")
- {
- if (self.pers["team"] == "allies")
- myTeamPlayers++;
- else
- enemyTeamPlayers++;
- }
- else if (player.pers["team"] == "axis")
+ // Get number of players
+ myTeamPlayers = 0;
+ enemyTeamPlayers = 0;
+ players = getentarray("player", "classname");
+ for(i = 0; i < players.size; i++)
{
- if (self.pers["team"] == "axis")
- myTeamPlayers++;
- else
- enemyTeamPlayers++;
+ player = players[i];
+
+ if (player.pers["team"] == "allies")
+ {
+ if (self.pers["team"] == "allies")
+ myTeamPlayers++;
+ else
+ enemyTeamPlayers++;
+ }
+ else if (player.pers["team"] == "axis")
+ {
+ if (self.pers["team"] == "axis")
+ myTeamPlayers++;
+ else
+ enemyTeamPlayers++;
+ }
}
+
+ teamPrefix = myTeamName + "_" + enemyTeamName;
+
+ // Show xVx text only if it is not 5v5
+ if (myTeamPlayers != 5 && enemyTeamPlayers != 5)
+ teamPrefix += "_" + myTeamPlayers + "v" + enemyTeamPlayers;
+ }
+ else
+ {
+ teamPrefix = getsubstr(getSecureString(self.name), 0, 18);
}
- // Shot xVx text only if it is not 5v5
- xVx = "";
- if (myTeamPlayers != 5 && enemyTeamPlayers != 5)
- xVx = "_" + myTeamPlayers + "v" + enemyTeamPlayers;
+
// Map name
mapname = level.mapname;
@@ -286,14 +296,14 @@ generateDemoName()
mapname = getsubstr(mapname, 0, 3);
}
- demoName = myTeamName + "_" + enemyTeamName+xVx + "_" + mapname + "#" + game["recordingMatchID"];
+ demoName = teamPrefix + "_" + mapname;
- // Avoiding file overwritting
if (level.gametype != "sd")
- {
demoName += "#" + level.gametype;
- }
+ demoName += "#" + game["recordingMatchID"];
+
+ // Avoiding file overwritting
if ((level.gametype == "sd" || level.gametype == "re") && game["roundsplayed"] > 0 && !game["overtime_active"])
demoName += "_r" + game["roundsplayed"]; // use round-number
else if (level.gametype != "sd" && level.gametype != "re" && isDefined(level.matchstarted) && level.matchstarted)
@@ -348,7 +358,7 @@ startRecordingForAll()
{
player = players[i];
- if (!player.pers["recording_executed"] && (player.sessionteam == "allies" || player.sessionteam == "axis"))
+ if (!player.pers["recording_executed"] && (player.pers["team"] == "allies" || player.pers["team"] == "axis"))
player thread execRecording();
}
}
@@ -433,32 +443,28 @@ stopRecording()
// Try to execute the command every second untill its confirmed by openscriptmenu
while(!self.pers["recording_stop_executed"])
{
- // Exec command on client side
- // If some menu is already opened:
- // - by player (main menu / quick messages) -> NOT WORKING - command will not be executed
- // - by player (by ESC key) -> that menu will be closed
- // - by script (via openMenu()) -> that menu will be closed and exec_cmd will not be closed correctly
- // (mouse will be visible with clear backgorund.... so closeMenu() is called to close that menu)
- self closeMenu();
- self closeInGameMenu();
- self setClientCvar2("exec_cmd", "stoprecord; openScriptMenu exec_cmd stop_recording");
- self openMenu(game["menu_exec_cmd"]); // open menu via script
- self closeMenu(); // will only close menu opened by script
+ // Open menu only if player is alive (to make sure no other menu is opened)
+ // Because stop recording may be called when matchinfo is cleared - it will close sevrerinfo menu for connected players
+ if (IsAlive(self))
+ {
+ // Exec command on client side
+ // If some menu is already opened:
+ // - by player (main menu / quick messages) -> NOT WORKING - command will not be executed
+ // - by player (by ESC key) -> that menu will be closed
+ // - by script (via openMenu()) -> that menu will be closed and exec_cmd will not be closed correctly
+ // (mouse will be visible with clear backgorund.... so closeMenu() is called to close that menu)
+ self closeMenu();
+ self closeInGameMenu();
+ self setClientCvar2("exec_cmd", "stoprecord; openScriptMenu exec_cmd stop_recording");
+ self openMenu(game["menu_exec_cmd"]); // open menu via script
+ self closeMenu(); // will only close menu opened by script
+ }
// Wait a second before next menu opening
for (i = 0; i < 9 && !self.pers["recording_executed"]; i++)
wait level.fps_multiplier * .1;
}
- // Serverinfo menu reopen
- // Because stop recording may be called when matchinfo is cleared - it will close sevrerinfo menu for connected players
- // If they are still in serverinfo menu, open that menu again
- if(!isDefined(self.pers["skipserverinfo"]))
- {
- self closeMenu();
- self openMenu(game["menu_serverinfo"]);
- }
-
self iprintln("Recording stopped.");
self show(); // show recording will start automaticallly again
diff --git a/source/maps/mp/gametypes/_round_report.gsc b/source/maps/mp/gametypes/_round_report.gsc
index 1d2b18b..d2472b3 100644
--- a/source/maps/mp/gametypes/_round_report.gsc
+++ b/source/maps/mp/gametypes/_round_report.gsc
@@ -138,6 +138,7 @@ onPlayerDamaged(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon,
lastDamage.wasKilled = false;
lastDamage.teamkill = (self != eAttacker) && (self.pers["team"] == eAttacker.pers["team"]);
lastDamage.bombPlanted = level.bombplanted;
+ lastDamage.adjustedBy = eAttacker.hitData[self getEntityNumber()].adjustedBy;
lastDamage.time = gettime();
lastDamage.firstTime = gettime();
@@ -289,31 +290,15 @@ print()
{
log = self.round_report_array[i];
-
-
string = getTimeString(log.time, log.bombPlanted) + " ^9";
-
- /*
-
- [Kar98k] hit to Body
- [Kar98k] kill to Body
- [Kar98k] kill to Body, first [Kar98k] hit to Leg
- [Pistol] kill to leg, first [Kar98k] hit to Leg
- [MP44] kill to Head, first [Grenade] hit
- [Bash] kill to Head, first [Bash] hit to Head
-
- hit to Body (Kar98k)
- kill to Body [Kar98k]
- kill to Body [Kar98k], first hit to Leg [Kar98k]
- kill to leg [Pistol], first hit to Leg [Kar98k]
- kill to Head [MP44], first hit [Grenade]
- kill to Head [Bash], first hit to Head [Bash]
-
- */
-
if (log.wasKilled)
{
+ // kill 1 to Arm (M1 Garant)
+ // kill 2 with 1 pellet (M1 Garant)
+ // kill 3 237hp to Head (KAR)
+ // kill 4 to Arm (M1 Garant), first hit 45hp to Leg
+
// Kill
if (log.teamkill)
string += "^1team kill " + killNum + "^9";
@@ -321,19 +306,26 @@ print()
string += "^2kill " + killNum + "^9";
killNum++;
+ if (log.multipleDamage == false)
+ {
+ damage = int(log.firstDamageValue);
+ if (damage == 0) damage = 1;
+ string += " " + damage + "hp";
+ }
+
// To
- if (log.sWeapon != "shotgun_mp")
+ if (log.sWeapon == "shotgun_mp" && log.sMeansOfDeath == "MOD_PISTOL_BULLET")
+ {
+ string += " with " + log.pellets + " pellet";
+ if (log.pellets > 1) string += "s";
+ }
+ else
{
if (isDefined(hitLocTexts[log.hitLoc]))
string += " to " + hitLocTexts[log.hitLoc];
else if (log.hitLoc != "none")
string += " to " + log.hitLoc;
}
- else
- {
- string += " with " + log.pellets + " pellet";
- if (log.pellets > 1) string += "s";
- }
// [Weapon]
weapon = getWeapon(log.sMeansOfDeath, log.sWeapon);
@@ -350,16 +342,25 @@ print()
{
if (log.teamkill)
string += "^1team^9 ";
-
string += "hit";
- //if (log.sMeansOfDeath == "MOD_RIFLE_BULLET" || log.sFirstWeapon == "shotgun_mp")
- string += " " + int(log.firstDamageValue) + "hp";
+ damage = int(log.firstDamageValue);
+ if (damage == 0) damage = 1;
+ string += " " + damage + "hp";
- if (log.sFirstWeapon == "shotgun_mp")
+ if (log.sFirstWeapon == "shotgun_mp" && log.sFirstMeansOfDeath == "MOD_PISTOL_BULLET")
{
string += " " + log.pellets + " pellet";
if (log.pellets > 1) string += "s";
+
+ if (log.adjustedBy == "consistent_shotgun_1_kill" || log.adjustedBy == "consistent_shotgun_1_hit")
+ string += " range-1";
+ else if (log.adjustedBy == "consistent_shotgun_2")
+ string += " range-2";
+ else if (log.adjustedBy == "consistent_shotgun_3")
+ string += " range-3";
+ else if (log.adjustedBy == "consistent_shotgun_4")
+ string += " range-4";
}
if (log.sFirstWeapon != "shotgun_mp")
@@ -368,7 +369,6 @@ print()
string += " to " + hitLocTexts[log.firstHitLoc];
else if (log.firstHitLoc != "none")
string += " to " + log.firstHitLoc;
-
}
// Show weapon if only hit is showed or weapons are different
@@ -381,6 +381,12 @@ print()
}
+ // Hit was adjusted by fixes
+ if (log.adjustedBy == "hand_hitbox_fix" || log.adjustedBy == "torso_hitbox_fix")
+ {
+ string += " ^1*";
+ }
+
self iprintln(string);
}
diff --git a/source/maps/mp/gametypes/_spectating_auto.gsc b/source/maps/mp/gametypes/_spectating_auto.gsc
index 8b39d90..0957a27 100644
--- a/source/maps/mp/gametypes/_spectating_auto.gsc
+++ b/source/maps/mp/gametypes/_spectating_auto.gsc
@@ -5,6 +5,7 @@ init()
if(game["firstInit"])
{
precacheString2("STRING_AUTO_SPECTATING", &"Auto-spectating");
+ precacheString2("STRING_AUTO_SPECTATING_OFF", &"Auto-spectating off");
precacheString2("STRING_AUTO_SPECTATING_REASON_MANUAL", &"Manual change");
precacheString2("STRING_AUTO_SPECTATING_REASON_VISIBLE_ENEMY", &"Enemy in sight");
precacheString2("STRING_AUTO_SPECTATING_REASON_VISIBLE_BY_ENEMY", &"Spotted by enemy");
@@ -18,15 +19,14 @@ init()
game["STRING_AUTO_SPECT_IS_ENABLED"] = "Auto-spectator is ^2On";
game["STRING_AUTO_SPECT_IS_DISABLED"] = "Auto-spectator is ^1Off";
- game["STRING_AUTO_SPECT_NAMES_ON"] = "Player names ^2On";
- game["STRING_AUTO_SPECT_NAMES_OFF"] = "Player names ^1Off";
+ game["STRING_AUTO_SPECT_NAMES_ON"] = "XRAY ^2On";
+ game["STRING_AUTO_SPECT_NAMES_OFF"] = "XRAY ^1Off";
}
level.autoSpectating_do = false;
level.autoSpectating_ID = -1; // spectated player
level.autoSpectating_spectatedPlayer = undefined;
level.autoSpectating_noSwitchUntill = 0;
- level.autoSpectating_refreshPlayerNames = false;
level.autoSpectating_HUD_text = "";
if (!isDefined(game["spectatingSystem_recommandedPlayerMode_teamLeft_player"]) || level.gametype != "sd" || game["round"] == 0) // round in case of bash
@@ -45,16 +45,16 @@ init()
addEventListener("onSpawnedSpectator", ::onSpawnedSpectator);
addEventListener("onPlayerDamaged", ::onPlayerDamaged);
addEventListener("onPlayerKilled", ::onPlayerKilled);
+
+ //setCvar("debug_spectator", 1);
}
onConnected()
{
if (!isDefined(self.pers["autoSpectating"]))
self.pers["autoSpectating"] = false;
-
self.autoSpectating_inCinematic = false;
self.freeSpectating = true; // free spectating, not following any player
- self.autoSpectating_ESP = true;
}
onConnectedAll()
@@ -76,7 +76,6 @@ onSpawnedSpectator()
onSpawnedPlayer()
{
- level.autoSpectating_refreshPlayerNames = true;
}
@@ -191,9 +190,9 @@ autoSpectator()
autoSpectatorEnable(); // enable by default
- self thread playerNames();
+ self thread maps\mp\gametypes\_spectating_hud_esp::ESP_Loop();
- self thread keys_help();
+ self thread maps\mp\gametypes\_spectating_hud::keys_help();
wait level.frame* 3;
@@ -204,6 +203,8 @@ autoSpectator()
hud_text_last = "";
+ angles_last = undefined;
+
for(;;)
{
wait level.frame;
@@ -230,14 +231,13 @@ autoSpectator()
// Wait untill timeout is over
if (level.in_readyup)
{
- autoSpectatorDisable();
+ self autoSpectatorDisable();
+ self autoSpectatorExit();
while (level.in_readyup)
wait level.fps_multiplier * 1;
- autoSpectatorEnable();
-
- self thread playerNames();
+ self autoSpectatorEnable();
}
@@ -274,8 +274,6 @@ autoSpectator()
self.freeSpectating = false; // free spectating ended, now following a player
}
-
-
// Update text with follow reason
if (self.pers["autoSpectating"])
{
@@ -374,7 +372,6 @@ handleMeleeButtonPress()
if (!self.pers["autoSpectating"])
{
self.freeSpectating = true; // free spectating, not following any player
- self.autoSpectating_ESP = true; // by default enabled
if (level.debug_spectator) self iprintln("meleeBtn> self.freeSpectating = ^2true");
}
@@ -417,9 +414,9 @@ handleUseButtonPress()
if (holded)
{
- self.autoSpectating_ESP = !self.autoSpectating_ESP;
- if (self.autoSpectating_ESP) self iprintln(game["STRING_AUTO_SPECT_NAMES_ON"]);
- else self iprintln(game["STRING_AUTO_SPECT_NAMES_OFF"]);
+ self.pers["autoSpectatingESP"] = !self.pers["autoSpectatingESP"];
+ if (self.pers["autoSpectatingESP"]) self iprintln(game["STRING_AUTO_SPECT_NAMES_ON"]);
+ else self iprintln(game["STRING_AUTO_SPECT_NAMES_OFF"]);
}
else
{
@@ -446,248 +443,6 @@ handleUseButtonPress()
-keys_help()
-{
- self endon("disconnect");
-
- self thread maps\mp\gametypes\_spectating_hud::keys_show();
-
- wait level.fps_multiplier * 5;
-
- self thread maps\mp\gametypes\_spectating_hud::keys_hide();
-}
-
-
-
-
-
-
-
-playerNames()
-{
- self endon("disconnect");
-
- wait level.frame;
-
- // Wait untill readyup is over (used when timeout is called in time based gametypes and somebody connect)
- while(level.in_readyup || isDefined(self.spec_waypoint))
- wait level.fps_multiplier * 1;
-
- level.autoSpectating_refreshPlayerNames = false;
- self.spec_waypoint = [];
-
- players = getentarray("player", "classname");
- for(i = 0; i < players.size; i++)
- {
- player = players[i];
- if (player != self && (player.pers["team"] == "allies" || player.pers["team"] == "axis") && player.sessionstate == "playing")
- {
- index = self.spec_waypoint.size;
- self.spec_waypoint[index] = addHUDClient(self, 10, 10, 1.2, (1,1,1), "center", "bottom", "subleft", "subtop");
- self.spec_waypoint[index].name = "name";
- self.spec_waypoint[index].player = player;
- self.spec_waypoint[index].fontscale = 0.75;
- self.spec_waypoint[index].archived = false;
- self.spec_waypoint[index] SetPlayerNameString(player);
- self.spec_waypoint[index] thread SetPlayerWaypoint(self, player, (0, 0, 10));
- self.spec_waypoint[index] thread hud_watchPlayer(self, player);
- }
- }
-
- index = self.spec_waypoint.size;
- self.spec_waypoint[index] = addHUDClient(self, 0, 0, undefined, (1,1,1), "center", "middle", "center", "middle");
- self.spec_waypoint[index].name = "cross";
- self.spec_waypoint[index].player = self;
- self.spec_waypoint[index] setShader("white", 2, 2);
- self.spec_waypoint[index] thread hud_watchPlayer(self);
-
-
- self.spec_follow_text = addHUDClient(self, 0, -85, 1.2, (1,1,1), "center", "middle", "center", "bottom");
- self.spec_follow_text.fontscale = 1;
- self.spec_follow_text.alpha = 0;
-
-
-
- waypoints = self.spec_waypoint.size;
- saved_player_last = undefined;
-
- for(;;)
- {
- wait level.frame;
-
- // All needs to be removed (changed team or new player connected)
- if (self.pers["team"] != "spectator" || level.in_readyup || level.autoSpectating_refreshPlayerNames)
- {
- break;
- }
-
- saved_dist2D = 0;
- saved_dist3D = 0;
- saved_player = undefined;
-
- for(i = 0; i < waypoints; i++)
- {
- hud = self.spec_waypoint[i];
-
- // If HUD is not defined, it means player disconnect and HUD object was deleted
- if (!isDefined(hud))
- continue;
-
- if (hud.name != "name" || !isDefined(hud.player) || !isAlive(hud.player))
- continue;
-
- self.spec_waypoint[i].paused = !self.autoSpectating_ESP && !self.freeSpectating && self.pers["autoSpectating"];
-
- if (!self.freeSpectating)
- continue;
-
-
- dist3D = distance(self.origin, hud.player.origin);
-
- // If text is somewhere in rectangle around center
- if ((hud.x > 160 && hud.x < 480 && hud.y > 120 && hud.y < 360) || (dist3D < 300 && hud.x > 160 && hud.x < 480))
- {
- if (dist3D < 1000)
- {
- dist2D = distance((hud.x, hud.y, 0), (320, 240, 0)); // Distance of text from center - text is in 640x480 rectangle aligned left top
-
- if (!isDefined(saved_player) || (dist2D < saved_dist2D && dist3D < saved_dist3D))
- {
- saved_dist2D = dist2D;
- saved_dist3D = dist3D;
- saved_player = hud.player;
- }
- }
- }
- }
-
- time = 0.1;
-
- if (isDefined(saved_player))
- {
- if (isDefined(saved_player_last) && saved_player_last != saved_player)
- {
- self.spec_follow_text fadeOverTime(time);
- self.spec_follow_text.alpha = 0;
- wait level.fps_multiplier * time;
- }
- if (!isDefined(saved_player_last) || saved_player_last != saved_player)
- {
- self.spec_follow_text SetPlayerNameString(saved_player);
- self.spec_follow_text fadeOverTime(time);
- self.spec_follow_text.alpha = 1;
- wait level.fps_multiplier * time;
- }
- }
- else if (isDefined(saved_player_last))
- {
- self.spec_follow_text fadeOverTime(time);
- self.spec_follow_text.alpha = 0;
- wait level.fps_multiplier * time;
- }
-
- saved_player_last = saved_player;
-
- self.spec_follow = saved_player;
- }
-
-
- // Destroy all
- for(i = 0; i < waypoints; i++)
- {
- hud = self.spec_waypoint[i];
-
- // If HUD is not defined, it means player disconnect and HUD object was deleted
- if (!isDefined(hud))
- continue;
-
- hud destroy2();
- }
- self.spec_waypoint = undefined;
-
- self.spec_follow_text destroy2();
-
- // Run again
- if (level.autoSpectating_refreshPlayerNames)
- {
- level.autoSpectating_refreshPlayerNames = false;
- self thread playerNames();
- }
-}
-
-
-// self is HUD, spectator is hud owner and player is waypoined player
-hud_watchPlayer(spectator, player)
-{
- for (;;)
- {
- if (!isDefined(self))
- break;
-
- if (!isDefined(player))
- {
- self destroy2();
- break;
- }
-
- if (self.name == "name")
- {
- color = (0.8, 0.8, 0.8);
- if (player.sessionteam == "allies")
- {
- if(game["allies"] == "american")
- color = (0.4, 0.9, .56);
- else if(game["allies"] == "british")
- color = (0.45, 0.73, 1);
- else if(game["allies"] == "russian")
- color = (1, 0.4, 0.4);
- }
- self.color = color;
-
- // ESP for spectated player - show only enemy
- alpha = 0;
- if (!isDefined(spectator.killcam) && spectator.autoSpectating_ESP && !spectator.freeSpectating && spectator.pers["autoSpectating"])
- {
- if(isAlive(player) && isDefined(level.autoSpectating_spectatedPlayer) && level.autoSpectating_spectatedPlayer.sessionteam != player.sessionteam)
- {
- alpha = 1;
-
- // If is in center, add alpha
- dist2D = distance((self.x, self.y, 0), (320, 240, 0)); // Distance of text from center - text is in 640x480 rectangle aligned left top
- if (dist2D < 100)
- {
- alpha = dist2D / 100;
- }
- }
- }
- else if (!isDefined(spectator.killcam) && spectator.freeSpectating)
- {
- if (isAlive(player))
- alpha = 1;
- else
- alpha = 0.4;
- }
-
- self.alpha = alpha;
- }
-
- if (self.name == "cross")
- {
- if (spectator.freeSpectating)
- self.alpha = 1;
- else
- self.alpha = 0;
- }
-
- wait level.frame;
- }
-}
-
-
-
-
-
-
autoSpectatorEnable()
{
@@ -705,7 +460,6 @@ autoSpectatorEnable()
self.autoSpectatingBG.y = 40;
self.autoSpectatingBG.horzAlign = "center";
self.autoSpectatingBG.vertAlign = "top";
- self.autoSpectatingBG.alpha = 0.5;
self.autoSpectatingBG setShader("black", 80, 10);
}
@@ -721,11 +475,12 @@ autoSpectatorEnable()
self.autoSpectatingText.y = 42;
self.autoSpectatingText.archived = false;
self.autoSpectatingText.font = "default";
- self.autoSpectatingText.sort = 2;
self.autoSpectatingText.alpha = 0.9;
+ self.autoSpectatingText.sort = 2;
self.autoSpectatingText.fontscale = 0.6;
}
+ self.autoSpectatingBG.alpha = 0.5;
self.autoSpectatingText setText(game["STRING_AUTO_SPECTATING"]);
}
@@ -735,17 +490,8 @@ autoSpectatorDisable()
self.pers["autoSpectating"] = false;
self.spectatorclient = -1;
-
- if(isdefined(self.autoSpectatingBG))
- {
- self.autoSpectatingBG destroy2();
- self.autoSpectatingBG = undefined;
- }
- if(isdefined(self.autoSpectatingText))
- {
- self.autoSpectatingText destroy2();
- self.autoSpectatingText = undefined;
- }
+ self.autoSpectatingBG.alpha = 0;
+ self.autoSpectatingText setText(game["STRING_AUTO_SPECTATING_OFF"]);
}
autoSpectatorExit()
diff --git a/source/maps/mp/gametypes/_spectating_hud.gsc b/source/maps/mp/gametypes/_spectating_hud.gsc
index c1f8421..e108ba7 100644
--- a/source/maps/mp/gametypes/_spectating_hud.gsc
+++ b/source/maps/mp/gametypes/_spectating_hud.gsc
@@ -9,26 +9,10 @@ init()
if(game["firstInit"])
{
- precacheString2("STRING_AUTO_SPECTATOR_KEY", &"Press ^3[{+melee_breath}]^7 to disable auto-spectator\nHold ^3[{+attack}]^7 to enable auto-spectator\nPress ^3[{+activate}]^7 to play killcam\nHold ^3[{+activate}]^7 to toggle player names");
-
- precacheString2("STRING_AUTO_SPECTATOR_HIT_HEAD", &"Head");
- precacheString2("STRING_AUTO_SPECTATOR_HIT_NECK", &"Neck");
- precacheString2("STRING_AUTO_SPECTATOR_HIT_BODY_UPPER", &"Body");
- precacheString2("STRING_AUTO_SPECTATOR_HIT_BODY_LOWE", &"Body");
- precacheString2("STRING_AUTO_SPECTATOR_HIT_SHOULDER", &"Shoulder");
- precacheString2("STRING_AUTO_SPECTATOR_HIT_ARM", &"Arm");
- precacheString2("STRING_AUTO_SPECTATOR_HIT_HAND", &"Hand");
- precacheString2("STRING_AUTO_SPECTATOR_HIT_LEG_UPPEER", &"Leg");
- precacheString2("STRING_AUTO_SPECTATOR_HIT_LEG_LOWER", &"Leg");
- precacheString2("STRING_AUTO_SPECTATOR_HIT_FOOT", &"Foot");
- precacheString2("STRING_AUTO_SPECTATOR_HIT_GRENADE", &"Grenade");
-
- precacheShader("headicon_dead");
+ precacheString2("STRING_AUTO_SPECTATOR_KEY", &"Press ^3[{+melee_breath}]^7 to disable auto-spectator\nHold ^3[{+attack}]^7 to enable auto-spectator\nPress ^3[{+activate}]^7 to play killcam\nHold ^3[{+activate}]^7 to toggle XRAY");
}
addEventListener("onSpawnedSpectator", ::onSpawnedSpectator);
- addEventListener("onPlayerDamaged", ::onPlayerDamaged);
- addEventListener("onPlayerKilled", ::onPlayerKilled);
}
onConnected()
@@ -53,316 +37,21 @@ onSpawnedSpectator()
}
-/*
-Called when player has taken damage.
-self is the player that took damage.
-*/
-onPlayerDamaged(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime)
-{
- if (isDefined(eAttacker) && isPlayer(eAttacker) && self != eAttacker && isDefined(level.autoSpectating_spectatedPlayer) && eAttacker == level.autoSpectating_spectatedPlayer && !level.in_readyup)
- {
- // Count total damage
- id = self getEntityNumber();
- if (!isDefined(eAttacker.autoSpectating_hits))
- eAttacker.autoSpectating_hits = [];
- if (!isDefined(eAttacker.autoSpectating_hits[id]))
- eAttacker.autoSpectating_hits[id] = 0;
- eAttacker.autoSpectating_hits[id] += iDamage;
- self thread resetDamage(eAttacker, id);
-
- // Show to all spectators
- players = getentarray("player", "classname");
- for(i = 0; i < players.size; i++)
- {
- player = players[i];
- if (player.pers["team"] == "spectator" && player.pers["autoSpectating"])
- {
- player thread showDamageInfo(eAttacker.autoSpectating_hits[id], sHitLoc, sMeansOfDeath);
- }
- }
- }
-
-
- if (isDefined(level.autoSpectating_spectatedPlayer) && self == level.autoSpectating_spectatedPlayer && !level.in_readyup)
- {
- // Show to all spectators
- players = getentarray("player", "classname");
- for(i = 0; i < players.size; i++)
- {
- player = players[i];
- if (player.pers["team"] == "spectator" && player.pers["autoSpectating"])
- {
- player thread showTakenDamageInfo((self.maxhealth - self.health) + iDamage, sHitLoc, sMeansOfDeath);
- }
- }
- }
-}
-
-/*
-Called when player is killed
-self is the player that was killed.
-*/
-onPlayerKilled(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration)
-{
- if (isDefined(eAttacker) && isPlayer(eAttacker) && self != eAttacker && isDefined(level.autoSpectating_spectatedPlayer) && eAttacker == level.autoSpectating_spectatedPlayer && !level.in_readyup)
- {
- // Show to all spectators
- players = getentarray("player", "classname");
- for(i = 0; i < players.size; i++)
- {
- player = players[i];
- if (player.pers["team"] == "spectator" && player.pers["autoSpectating"])
- {
- player thread showKillInfo();
- }
- }
- }
-}
-
-
-
-
-resetDamage(eAttacker, id)
-{
- //self endon("disconnect");
- eAttacker endon("disconnect");
-
- self notify("autoSpectating_resetDamage_end");
- self endon("autoSpectating_resetDamage_end");
-
- wait level.fps_multiplier * 5;
-
- eAttacker.autoSpectating_hits[id] = undefined;
-}
-
-
-showDamageInfo(iDamage, sHitLoc, sMeansOfDeath)
-{
- hitLocTexts["head"] = game["STRING_AUTO_SPECTATOR_HIT_HEAD"];
- hitLocTexts["neck"] = game["STRING_AUTO_SPECTATOR_HIT_NECK"];
- hitLocTexts["torso_upper"] = game["STRING_AUTO_SPECTATOR_HIT_BODY_UPPER"];
- hitLocTexts["torso_lower"] = game["STRING_AUTO_SPECTATOR_HIT_BODY_LOWE"];
- hitLocTexts["left_arm_upper"] = game["STRING_AUTO_SPECTATOR_HIT_SHOULDER"];
- hitLocTexts["left_arm_lower"] = game["STRING_AUTO_SPECTATOR_HIT_ARM"];
- hitLocTexts["left_hand"] = game["STRING_AUTO_SPECTATOR_HIT_HAND"];
- hitLocTexts["right_arm_upper"] = game["STRING_AUTO_SPECTATOR_HIT_SHOULDER"];
- hitLocTexts["right_arm_lower"] = game["STRING_AUTO_SPECTATOR_HIT_ARM"];
- hitLocTexts["right_hand"] = game["STRING_AUTO_SPECTATOR_HIT_HAND"];
- hitLocTexts["left_leg_upper"] = game["STRING_AUTO_SPECTATOR_HIT_LEG_UPPEER"];
- hitLocTexts["left_leg_lower"] = game["STRING_AUTO_SPECTATOR_HIT_LEG_LOWER"];
- hitLocTexts["left_foot"] = game["STRING_AUTO_SPECTATOR_HIT_FOOT"];
- hitLocTexts["right_leg_upper"] = game["STRING_AUTO_SPECTATOR_HIT_LEG_UPPEER"];
- hitLocTexts["right_leg_lower"] = game["STRING_AUTO_SPECTATOR_HIT_LEG_LOWER"];
- hitLocTexts["right_foot"] = game["STRING_AUTO_SPECTATOR_HIT_FOOT"];
-
- self endon("disconnect");
-
- if (isDefined(self.killcam))
- return;
-
- self notify("autoSpectating_showDamageInfo_end");
- self endon("autoSpectating_showDamageInfo_end");
-
- if (!isDefined(self.hud_damageinfo))
- {
- self.hud_damageinfo = newClientHudElem2(self);
- self.hud_damageinfo.horzAlign = "center";
- self.hud_damageinfo.vertAlign = "middle";
- self.hud_damageinfo.x = 50;
- self.hud_damageinfo.fontscale = 1.2;
- self.hud_damageinfo.archived = false;
- }
-
- if (!isDefined(self.hud_damageinfo_loc))
- {
- self.hud_damageinfo_loc = newClientHudElem2(self);
- self.hud_damageinfo_loc.horzAlign = "center";
- self.hud_damageinfo_loc.vertAlign = "middle";
- self.hud_damageinfo_loc.x = 80;
- self.hud_damageinfo_loc.fontscale = 1.1;
- self.hud_damageinfo_loc.archived = false;
- }
-
- if (iDamage > 100)
- iDamage = 100;
-
- self.hud_damageinfo setValue(iDamage);
-
- if (sMeansOfDeath == "MOD_GRENADE_SPLASH")
- self.hud_damageinfo_loc setText(game["STRING_AUTO_SPECTATOR_HIT_GRENADE"]);
- else if (isDefined(hitLocTexts[sHitLoc]))
- self.hud_damageinfo_loc setText(hitLocTexts[sHitLoc]);
-
- r = 1;
- g = 1 - ((iDamage - 50) / 50); if (g < 0) g = 0; if (g > 1) g = 1;
- b = 1 - (iDamage / 50); if (b < 0) b = 0; if (b > 1) b = 1;
-
- // red to orange to yellow -> add green
- // yellow to white -> add blue
-
- self.hud_damageinfo.color = (r, g, b);
- self.hud_damageinfo_loc.color = (1, 1, 1);
-
- self.hud_damageinfo.y = 0;
- self.hud_damageinfo.alpha = 1;
- self.hud_damageinfo_loc.y = 0;
- self.hud_damageinfo_loc.alpha = 1;
-
- self.hud_damageinfo moveovertime(2);
- self.hud_damageinfo.y = -50;
- self.hud_damageinfo_loc moveovertime(2);
- self.hud_damageinfo_loc.y = -50;
-
- wait level.fps_multiplier * 1;
- self.hud_damageinfo FadeOverTime(1);
- self.hud_damageinfo.alpha = 0;
- self.hud_damageinfo_loc FadeOverTime(1);
- self.hud_damageinfo_loc.alpha = 0;
- wait level.fps_multiplier * 2;
- self.hud_damageinfo destroy2();
- self.hud_damageinfo_loc destroy2();
-}
-
-
-
-showTakenDamageInfo(iDamage, sHitLoc, sMeansOfDeath)
-{
- hitLocTexts["head"] = game["STRING_AUTO_SPECTATOR_HIT_HEAD"];
- hitLocTexts["neck"] = game["STRING_AUTO_SPECTATOR_HIT_NECK"];
- hitLocTexts["torso_upper"] = game["STRING_AUTO_SPECTATOR_HIT_BODY_UPPER"];
- hitLocTexts["torso_lower"] = game["STRING_AUTO_SPECTATOR_HIT_BODY_LOWE"];
- hitLocTexts["left_arm_upper"] = game["STRING_AUTO_SPECTATOR_HIT_SHOULDER"];
- hitLocTexts["left_arm_lower"] = game["STRING_AUTO_SPECTATOR_HIT_ARM"];
- hitLocTexts["left_hand"] = game["STRING_AUTO_SPECTATOR_HIT_HAND"];
- hitLocTexts["right_arm_upper"] = game["STRING_AUTO_SPECTATOR_HIT_SHOULDER"];
- hitLocTexts["right_arm_lower"] = game["STRING_AUTO_SPECTATOR_HIT_ARM"];
- hitLocTexts["right_hand"] = game["STRING_AUTO_SPECTATOR_HIT_HAND"];
- hitLocTexts["left_leg_upper"] = game["STRING_AUTO_SPECTATOR_HIT_LEG_UPPEER"];
- hitLocTexts["left_leg_lower"] = game["STRING_AUTO_SPECTATOR_HIT_LEG_LOWER"];
- hitLocTexts["left_foot"] = game["STRING_AUTO_SPECTATOR_HIT_FOOT"];
- hitLocTexts["right_leg_upper"] = game["STRING_AUTO_SPECTATOR_HIT_LEG_UPPEER"];
- hitLocTexts["right_leg_lower"] = game["STRING_AUTO_SPECTATOR_HIT_LEG_LOWER"];
- hitLocTexts["right_foot"] = game["STRING_AUTO_SPECTATOR_HIT_FOOT"];
-
- self endon("disconnect");
-
- if (isDefined(self.killcam))
- return;
-
- self notify("autoSpectating_showTakenDamageInfo_end");
- self endon("autoSpectating_showTakenDamageInfo_end");
-
- if (!isDefined(self.hud_takendamageinfo))
- {
- self.hud_takendamageinfo = newClientHudElem2(self);
- self.hud_takendamageinfo.horzAlign = "center";
- self.hud_takendamageinfo.vertAlign = "middle";
- self.hud_takendamageinfo.x = -20;
- self.hud_takendamageinfo.fontscale = 1.2;
- self.hud_takendamageinfo.archived = false;
- }
-
- if (!isDefined(self.hud_takendamageinfo_loc))
- {
- self.hud_takendamageinfo_loc = newClientHudElem2(self);
- self.hud_takendamageinfo_loc.horzAlign = "center";
- self.hud_takendamageinfo_loc.vertAlign = "middle";
- self.hud_takendamageinfo_loc.x = 10;
- self.hud_takendamageinfo_loc.fontscale = 1.1;
- self.hud_takendamageinfo_loc.archived = false;
- }
-
- if (iDamage > 100)
- iDamage = 100;
-
- self.hud_takendamageinfo setValue(iDamage*-1);
-
- if (sMeansOfDeath == "MOD_GRENADE_SPLASH")
- self.hud_takendamageinfo_loc setText(game["STRING_AUTO_SPECTATOR_HIT_GRENADE"]);
- else if (isDefined(hitLocTexts[sHitLoc]))
- self.hud_takendamageinfo_loc setText(hitLocTexts[sHitLoc]);
-
- r = 1;
- g = 1 - ((iDamage - 50) / 50); if (g < 0) g = 0; if (g > 1) g = 1;
- b = 1 - (iDamage / 50); if (b < 0) b = 0; if (b > 1) b = 1;
-
- // red to orange to yellow -> add green
- // yellow to white -> add blue
-
- self.hud_takendamageinfo.color = (r, g, b);
- self.hud_takendamageinfo_loc.color = (1, 1, 1);
-
- self.hud_takendamageinfo.y = 80;
- self.hud_takendamageinfo.alpha = 1;
- self.hud_takendamageinfo_loc.y = 80;
- self.hud_takendamageinfo_loc.alpha = 1;
-
- self.hud_takendamageinfo moveovertime(2);
- self.hud_takendamageinfo.y = 120;
- self.hud_takendamageinfo_loc moveovertime(2);
- self.hud_takendamageinfo_loc.y = 120;
-
- wait level.fps_multiplier * 1;
-
- self.hud_takendamageinfo FadeOverTime(1);
- self.hud_takendamageinfo.alpha = 0;
- self.hud_takendamageinfo_loc FadeOverTime(1);
- self.hud_takendamageinfo_loc.alpha = 0;
-
- wait level.fps_multiplier * 2;
-
- self.hud_takendamageinfo destroy2();
- self.hud_takendamageinfo_loc destroy2();
-}
-
-
-
-
-showKillInfo()
+keys_help()
{
self endon("disconnect");
- if (isDefined(self.killcam))
- return;
-
- self notify("autoSpectating_showKillInfo_end");
- self endon("autoSpectating_showKillInfo_end");
+ self thread keys_show();
- if (!isDefined(self.hud_damageinfo_kill))
- {
- self.hud_damageinfo_kill = newClientHudElem2(self);
- self.hud_damageinfo_kill.horzAlign = "center";
- self.hud_damageinfo_kill.vertAlign = "middle";
- self.hud_damageinfo_kill.x = 30;
- self.hud_damageinfo_kill.archived = false;
- self.hud_damageinfo_kill setShader("headicon_dead", 23, 23);
- }
-
- self.hud_damageinfo_kill.y = 0;
- self.hud_damageinfo_kill.alpha = 1;
- self.hud_damageinfo_kill moveovertime(2);
- self.hud_damageinfo_kill.y = -50;
-
- wait level.fps_multiplier * 1;
-
- self.hud_damageinfo_kill FadeOverTime(1);
- self.hud_damageinfo_kill.alpha = 0;
-
- wait level.fps_multiplier * 2;
+ wait level.fps_multiplier * 5;
- self.hud_damageinfo_kill destroy2();
+ self thread keys_hide();
}
-
-
-
-
-
-
keys_show()
{
if(!isdefined(self.autoSpectatingKeyInfoBG))
@@ -421,16 +110,6 @@ keys_hide()
-
-
-
-
-
-
-
-
-
-
HUD_hideUI()
{
diff --git a/source/maps/mp/gametypes/_spectating_hud_damage.gsc b/source/maps/mp/gametypes/_spectating_hud_damage.gsc
new file mode 100644
index 0000000..d16fa48
--- /dev/null
+++ b/source/maps/mp/gametypes/_spectating_hud_damage.gsc
@@ -0,0 +1,501 @@
+#include maps\mp\gametypes\global\_global;
+
+init()
+{
+ addEventListener("onPlayerDamaged", ::onPlayerDamaged); // In readyup show damage info for regular players
+
+ if(game["firstInit"])
+ {
+ precacheString2("STRING_AUTO_SPECTATOR_HIT_HP", &"&&1 hp");
+
+ precacheString2("STRING_AUTO_SPECTATOR_HIT_HEAD", &"Head");
+ precacheString2("STRING_AUTO_SPECTATOR_HIT_NECK", &"Neck");
+ precacheString2("STRING_AUTO_SPECTATOR_HIT_BODY", &"Torso");
+ precacheString2("STRING_AUTO_SPECTATOR_HIT_BODY_UPPER", &"Torso-up");
+ precacheString2("STRING_AUTO_SPECTATOR_HIT_BODY_LOWER", &"Torso-low");
+ precacheString2("STRING_AUTO_SPECTATOR_HIT_SHOULDER", &"Shoulder");
+ precacheString2("STRING_AUTO_SPECTATOR_HIT_ARM", &"Arm");
+ precacheString2("STRING_AUTO_SPECTATOR_HIT_HAND", &"Hand");
+ precacheString2("STRING_AUTO_SPECTATOR_HIT_LEG", &"Leg");
+ precacheString2("STRING_AUTO_SPECTATOR_HIT_LEG_UPPEER", &"Leg-up");
+ precacheString2("STRING_AUTO_SPECTATOR_HIT_LEG_LOWER", &"Leg-low");
+ precacheString2("STRING_AUTO_SPECTATOR_HIT_FOOT", &"Foot");
+ precacheString2("STRING_AUTO_SPECTATOR_HIT_GRENADE", &"Grenade");
+
+ precacheString2("STRING_AUTO_SPECTATOR_HIT_SHOTGUN_1p", &"1 pellet");
+ precacheString2("STRING_AUTO_SPECTATOR_HIT_SHOTGUN_2p", &"2 pellets");
+ precacheString2("STRING_AUTO_SPECTATOR_HIT_SHOTGUN_3p", &"3 pellets");
+ precacheString2("STRING_AUTO_SPECTATOR_HIT_SHOTGUN_4p", &"4 pellets");
+ precacheString2("STRING_AUTO_SPECTATOR_HIT_SHOTGUN_5p", &"5 pellets");
+ precacheString2("STRING_AUTO_SPECTATOR_HIT_SHOTGUN_6p", &"6 pellets");
+ precacheString2("STRING_AUTO_SPECTATOR_HIT_SHOTGUN_7p", &"7 pellets");
+ precacheString2("STRING_AUTO_SPECTATOR_HIT_SHOTGUN_8p", &"8 pellets");
+
+ precacheString2("STRING_AUTO_SPECTATOR_HIT_INFO_SHOTGUN_1_KILL", &"Range 1 (1 pellet needed for kill");
+ precacheString2("STRING_AUTO_SPECTATOR_HIT_INFO_SHOTGUN_1_HIT", &"Range 1 (2 pellets needed for kill (body not visible)");
+ precacheString2("STRING_AUTO_SPECTATOR_HIT_INFO_SHOTGUN_2", &"Range 2 (2 pellets needed for kill");
+ precacheString2("STRING_AUTO_SPECTATOR_HIT_INFO_SHOTGUN_3", &"Range 3 (3 pellets needed for kill");
+ precacheString2("STRING_AUTO_SPECTATOR_HIT_INFO_SHOTGUN_4", &"Range 4 (linear damage)");
+ precacheString2("STRING_AUTO_SPECTATOR_HIT_INFO_TORSO_HITBOX_FIX", &"Torso hitbox fix applied");
+ precacheString2("STRING_AUTO_SPECTATOR_HIT_INFO_HAND_HITBOX_FIX", &"Hand hitbox fix applied");
+
+ precacheShader("headicon_dead");
+ }
+
+ if (!level.spectatingSystem)
+ return;
+
+ addEventListener("onPlayerKilled", ::onPlayerKilled);
+}
+
+
+/*
+Called when player has taken damage.
+self is the player that took damage.
+*/
+onPlayerDamaged(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime)
+{
+ waittillframeend; // wait untill all hits are processed
+
+ if (isDefined(eAttacker) && isPlayer(eAttacker) && self != eAttacker)
+ {
+ // Damage info can be showed in 2 modes:
+ // - in readyup for regular players
+ // - for auto-spectators - show info for spectated player
+ if (level.in_readyup ||
+ (level.spectatingSystem && !level.in_readyup && isDefined(level.autoSpectating_spectatedPlayer) && eAttacker == level.autoSpectating_spectatedPlayer))
+ {
+ self_num = self getEntityNumber();
+ isShotgun = sWeapon == "shotgun_mp" && sMeansOfDeath == "MOD_PISTOL_BULLET";
+
+ pellets = 0;
+ if (isShotgun)
+ pellets = eAttacker.hitData[self_num].id;
+
+ adjustedBy = eAttacker.hitData[self_num].adjustedBy;
+
+ // In readyup for regular players
+ if (level.in_readyup)
+ {
+ // For shotgun show accumulated damage
+ if (isShotgun) damage = eAttacker.hitData[self_num].damage_comulated;
+ else damage = eAttacker.hitData[self_num].damage;
+
+ eAttacker thread showDamageInfoReadyup(damage, sHitLoc, sMeansOfDeath, pellets, adjustedBy);
+ }
+ else // For auto-spactators
+ {
+ damage = eAttacker.hitData[self_num].damage_comulated;
+
+ // Show to all spectators
+ players = getentarray("player", "classname");
+ for(i = 0; i < players.size; i++)
+ {
+ player = players[i];
+ if (player.pers["team"] == "spectator" && player.pers["autoSpectating"] && !isDefined(player.killcam))
+ {
+ player thread showDamageInfo(damage, sHitLoc, sMeansOfDeath, pellets, adjustedBy);
+ }
+ }
+ }
+ }
+ }
+
+ if (!level.spectatingSystem)
+ return;
+
+ if (isDefined(level.autoSpectating_spectatedPlayer) && self == level.autoSpectating_spectatedPlayer && !level.in_readyup)
+ {
+ damage = iDamage;
+ if (isDefined(eAttacker) && isPlayer(eAttacker))
+ {
+ self_num = self getEntityNumber();
+ damage = eAttacker.hitData[self_num].damage_comulated;
+ }
+
+ // Show to all spectators
+ players = getentarray("player", "classname");
+ for(i = 0; i < players.size; i++)
+ {
+ player = players[i];
+ if (player.pers["team"] == "spectator" && player.pers["autoSpectating"] && !isDefined(player.killcam))
+ {
+ player thread showTakenDamageInfo(damage, sHitLoc, sMeansOfDeath);
+ }
+ }
+ }
+}
+
+/*
+Called when player is killed
+self is the player that was killed.
+*/
+onPlayerKilled(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration)
+{
+ if (isDefined(eAttacker) && isPlayer(eAttacker) && self != eAttacker && isDefined(level.autoSpectating_spectatedPlayer) && eAttacker == level.autoSpectating_spectatedPlayer && !level.in_readyup)
+ {
+ // Show to all spectators
+ players = getentarray("player", "classname");
+ for(i = 0; i < players.size; i++)
+ {
+ player = players[i];
+ if (player.pers["team"] == "spectator" && player.pers["autoSpectating"])
+ {
+ player thread showKillInfo();
+ }
+ }
+ }
+}
+
+showDamageInfo(damage, sHitLoc, sMeansOfDeath, pellets, adjustedBy)
+{
+ hitLocTexts["head"] = game["STRING_AUTO_SPECTATOR_HIT_HEAD"];
+ hitLocTexts["neck"] = game["STRING_AUTO_SPECTATOR_HIT_NECK"];
+ hitLocTexts["torso_upper"] = game["STRING_AUTO_SPECTATOR_HIT_BODY"];
+ hitLocTexts["torso_lower"] = game["STRING_AUTO_SPECTATOR_HIT_BODY"];
+ hitLocTexts["left_arm_upper"] = game["STRING_AUTO_SPECTATOR_HIT_SHOULDER"];
+ hitLocTexts["left_arm_lower"] = game["STRING_AUTO_SPECTATOR_HIT_ARM"];
+ hitLocTexts["left_hand"] = game["STRING_AUTO_SPECTATOR_HIT_HAND"];
+ hitLocTexts["right_arm_upper"] = game["STRING_AUTO_SPECTATOR_HIT_SHOULDER"];
+ hitLocTexts["right_arm_lower"] = game["STRING_AUTO_SPECTATOR_HIT_ARM"];
+ hitLocTexts["right_hand"] = game["STRING_AUTO_SPECTATOR_HIT_HAND"];
+ hitLocTexts["left_leg_upper"] = game["STRING_AUTO_SPECTATOR_HIT_LEG"];
+ hitLocTexts["left_leg_lower"] = game["STRING_AUTO_SPECTATOR_HIT_LEG"];
+ hitLocTexts["right_leg_upper"] = game["STRING_AUTO_SPECTATOR_HIT_LEG"];
+ hitLocTexts["right_leg_lower"] = game["STRING_AUTO_SPECTATOR_HIT_LEG"];
+ hitLocTexts["left_foot"] = game["STRING_AUTO_SPECTATOR_HIT_FOOT"];
+ hitLocTexts["right_foot"] = game["STRING_AUTO_SPECTATOR_HIT_FOOT"];
+
+
+ self endon("disconnect");
+
+ if (isDefined(self.killcam))
+ return;
+
+ self notify("autoSpectating_showDamageInfo_end");
+ self endon("autoSpectating_showDamageInfo_end");
+
+ if (!isDefined(self.hud_damageinfo))
+ {
+ self.hud_damageinfo = newClientHudElem2(self);
+ self.hud_damageinfo.horzAlign = "center";
+ self.hud_damageinfo.vertAlign = "middle";
+ self.hud_damageinfo.alignX = "right";
+ self.hud_damageinfo.label = game["STRING_AUTO_SPECTATOR_HIT_HP"];
+ self.hud_damageinfo.x = -10;
+ self.hud_damageinfo.fontscale = 1.1;
+ self.hud_damageinfo.archived = false;
+ }
+
+ if (!isDefined(self.hud_damageinfo_loc))
+ {
+ self.hud_damageinfo_loc = newClientHudElem2(self);
+ self.hud_damageinfo_loc.horzAlign = "center";
+ self.hud_damageinfo_loc.vertAlign = "middle";
+ self.hud_damageinfo_loc.alignX = "left";
+ self.hud_damageinfo_loc.x = 2;
+ self.hud_damageinfo_loc.fontscale = 1.1;
+ self.hud_damageinfo_loc.archived = false;
+ }
+
+ if (damage > 100)
+ damage = 100;
+
+ self.hud_damageinfo setValue(damage);
+
+ if (sMeansOfDeath == "MOD_GRENADE_SPLASH")
+ self.hud_damageinfo_loc setText(game["STRING_AUTO_SPECTATOR_HIT_GRENADE"]);
+ else if (pellets >= 1 && pellets <= 8) // shotgun
+ self.hud_damageinfo_loc setText(game["STRING_AUTO_SPECTATOR_HIT_SHOTGUN_" + pellets + "p"]);
+ else if (isDefined(hitLocTexts[sHitLoc]))
+ self.hud_damageinfo_loc setText(hitLocTexts[sHitLoc]);
+
+ r = 1;
+ g = 1 - ((damage - 50) / 50); if (g < 0) g = 0; if (g > 1) g = 1;
+ b = 1 - (damage / 50); if (b < 0) b = 0; if (b > 1) b = 1;
+
+ // red to orange to yellow -> add green
+ // yellow to white -> add blue
+
+ self.hud_damageinfo.color = (r, g, b);
+ self.hud_damageinfo_loc.color = (1, 1, 1);
+
+ self.hud_damageinfo.y = 120;
+ self.hud_damageinfo.alpha = 1;
+ self.hud_damageinfo_loc.y = 120;
+ self.hud_damageinfo_loc.alpha = 1;
+
+ self.hud_damageinfo moveovertime(3);
+ self.hud_damageinfo.y = 100;
+ self.hud_damageinfo_loc moveovertime(3);
+ self.hud_damageinfo_loc.y = 100;
+
+ wait level.fps_multiplier * 1;
+
+ self.hud_damageinfo FadeOverTime(2);
+ self.hud_damageinfo.alpha = 0;
+ self.hud_damageinfo_loc FadeOverTime(2);
+ self.hud_damageinfo_loc.alpha = 0;
+
+ wait level.fps_multiplier * 2;
+
+ self.hud_damageinfo destroy2();
+ self.hud_damageinfo_loc destroy2();
+}
+
+
+
+showDamageInfoReadyup(damage, sHitLoc, sMeansOfDeath, pellets, adjustedBy)
+{
+ hitLocTexts["head"] = game["STRING_AUTO_SPECTATOR_HIT_HEAD"];
+ hitLocTexts["neck"] = game["STRING_AUTO_SPECTATOR_HIT_NECK"];
+ hitLocTexts["torso_upper"] = game["STRING_AUTO_SPECTATOR_HIT_BODY_UPPER"];
+ hitLocTexts["torso_lower"] = game["STRING_AUTO_SPECTATOR_HIT_BODY_LOWER"];
+ hitLocTexts["left_arm_upper"] = game["STRING_AUTO_SPECTATOR_HIT_SHOULDER"];
+ hitLocTexts["left_arm_lower"] = game["STRING_AUTO_SPECTATOR_HIT_ARM"];
+ hitLocTexts["left_hand"] = game["STRING_AUTO_SPECTATOR_HIT_HAND"];
+ hitLocTexts["right_arm_upper"] = game["STRING_AUTO_SPECTATOR_HIT_SHOULDER"];
+ hitLocTexts["right_arm_lower"] = game["STRING_AUTO_SPECTATOR_HIT_ARM"];
+ hitLocTexts["right_hand"] = game["STRING_AUTO_SPECTATOR_HIT_HAND"];
+ hitLocTexts["left_leg_upper"] = game["STRING_AUTO_SPECTATOR_HIT_LEG_UPPEER"];
+ hitLocTexts["left_leg_lower"] = game["STRING_AUTO_SPECTATOR_HIT_LEG_LOWER"];
+ hitLocTexts["right_leg_upper"] = game["STRING_AUTO_SPECTATOR_HIT_LEG_UPPEER"];
+ hitLocTexts["right_leg_lower"] = game["STRING_AUTO_SPECTATOR_HIT_LEG_LOWER"];
+ hitLocTexts["left_foot"] = game["STRING_AUTO_SPECTATOR_HIT_FOOT"];
+ hitLocTexts["right_foot"] = game["STRING_AUTO_SPECTATOR_HIT_FOOT"];
+
+ adjuestedByTexts["consistent_shotgun_1_kill"] = game["STRING_AUTO_SPECTATOR_HIT_INFO_SHOTGUN_1_KILL"];
+ adjuestedByTexts["consistent_shotgun_1_hit"] = game["STRING_AUTO_SPECTATOR_HIT_INFO_SHOTGUN_1_HIT"];
+ adjuestedByTexts["consistent_shotgun_2"] = game["STRING_AUTO_SPECTATOR_HIT_INFO_SHOTGUN_2"];
+ adjuestedByTexts["consistent_shotgun_3"] = game["STRING_AUTO_SPECTATOR_HIT_INFO_SHOTGUN_3"];
+ adjuestedByTexts["consistent_shotgun_4"] = game["STRING_AUTO_SPECTATOR_HIT_INFO_SHOTGUN_4"];
+ adjuestedByTexts["torso_hitbox_fix"] = game["STRING_AUTO_SPECTATOR_HIT_INFO_TORSO_HITBOX_FIX"];
+ adjuestedByTexts["hand_hitbox_fix"] = game["STRING_AUTO_SPECTATOR_HIT_INFO_HAND_HITBOX_FIX"];
+
+
+ self endon("disconnect");
+
+ if (isDefined(self.killcam))
+ return;
+
+ self notify("autoSpectating_showDamageInfo_end");
+ self endon("autoSpectating_showDamageInfo_end");
+
+ if (!isDefined(self.hud_damageinfo))
+ {
+ self.hud_damageinfo = newClientHudElem2(self);
+ self.hud_damageinfo.horzAlign = "center";
+ self.hud_damageinfo.vertAlign = "middle";
+ self.hud_damageinfo.alignX = "left";
+ self.hud_damageinfo.label = game["STRING_AUTO_SPECTATOR_HIT_HP"];
+ self.hud_damageinfo.x = -40;
+ self.hud_damageinfo.fontscale = 1.1;
+ self.hud_damageinfo.archived = false;
+ }
+
+ if (!isDefined(self.hud_damageinfo_loc))
+ {
+ self.hud_damageinfo_loc = newClientHudElem2(self);
+ self.hud_damageinfo_loc.horzAlign = "center";
+ self.hud_damageinfo_loc.vertAlign = "middle";
+ self.hud_damageinfo_loc.alignX = "right";
+ self.hud_damageinfo_loc.x = 40;
+ self.hud_damageinfo_loc.fontscale = 1.1;
+ self.hud_damageinfo_loc.archived = false;
+ }
+
+ if (!isDefined(self.hud_damageinfo_adjustedBy))
+ {
+ self.hud_damageinfo_adjustedBy = newClientHudElem2(self);
+ self.hud_damageinfo_adjustedBy.horzAlign = "center";
+ self.hud_damageinfo_adjustedBy.vertAlign = "middle";
+ self.hud_damageinfo_adjustedBy.alignX = "center";
+ self.hud_damageinfo_adjustedBy.x = 0;
+ self.hud_damageinfo_adjustedBy.fontscale = 0.7;
+ self.hud_damageinfo_adjustedBy.archived = false;
+ }
+
+ self.hud_damageinfo setValue(damage);
+
+ if (sMeansOfDeath == "MOD_GRENADE_SPLASH")
+ self.hud_damageinfo_loc setText(game["STRING_AUTO_SPECTATOR_HIT_GRENADE"]);
+ else if (pellets >= 1 && pellets <= 8) // shotgun
+ self.hud_damageinfo_loc setText(game["STRING_AUTO_SPECTATOR_HIT_SHOTGUN_" + pellets + "p"]);
+ else if (isDefined(hitLocTexts[sHitLoc]))
+ self.hud_damageinfo_loc setText(hitLocTexts[sHitLoc]);
+
+ if (isDefined(adjuestedByTexts[adjustedBy]))
+ {
+ self.hud_damageinfo_adjustedBy setText(adjuestedByTexts[adjustedBy]);
+ self.hud_damageinfo_adjustedBy.alpha = 1;
+ }
+ else
+ self.hud_damageinfo_adjustedBy.alpha = 0;
+
+ r = 1;
+ g = 1 - ((damage - 50) / 50); if (g < 0) g = 0; if (g > 1) g = 1;
+ b = 1 - (damage / 50); if (b < 0) b = 0; if (b > 1) b = 1;
+
+ // red to orange to yellow -> add green
+ // yellow to white -> add blue
+
+ self.hud_damageinfo.color = (r, g, b);
+ self.hud_damageinfo_loc.color = (1, 1, 1);
+ self.hud_damageinfo_adjustedBy.color = (1, 1, 1);
+
+ self.hud_damageinfo.y = 120;
+ self.hud_damageinfo.alpha = 1;
+ self.hud_damageinfo_loc.y = 120;
+ self.hud_damageinfo_loc.alpha = 1;
+ self.hud_damageinfo_adjustedBy.y = 110;
+ //self.hud_damageinfo_adjustedBy.alpha = 1;
+
+ wait level.fps_multiplier * 3;
+
+ self.hud_damageinfo FadeOverTime(1);
+ self.hud_damageinfo.alpha = 0;
+ self.hud_damageinfo_loc FadeOverTime(1);
+ self.hud_damageinfo_loc.alpha = 0;
+ self.hud_damageinfo_adjustedBy FadeOverTime(1);
+ self.hud_damageinfo_adjustedBy.alpha = 0;
+
+ wait level.fps_multiplier * 1;
+
+ self.hud_damageinfo destroy2();
+ self.hud_damageinfo_loc destroy2();
+ self.hud_damageinfo_adjustedBy destroy2();
+}
+
+
+
+showTakenDamageInfo(iDamage, sHitLoc, sMeansOfDeath)
+{
+ hitLocTexts["head"] = game["STRING_AUTO_SPECTATOR_HIT_HEAD"];
+ hitLocTexts["neck"] = game["STRING_AUTO_SPECTATOR_HIT_NECK"];
+ hitLocTexts["torso_upper"] = game["STRING_AUTO_SPECTATOR_HIT_BODY"];
+ hitLocTexts["torso_lower"] = game["STRING_AUTO_SPECTATOR_HIT_BODY"];
+ hitLocTexts["left_arm_upper"] = game["STRING_AUTO_SPECTATOR_HIT_SHOULDER"];
+ hitLocTexts["left_arm_lower"] = game["STRING_AUTO_SPECTATOR_HIT_ARM"];
+ hitLocTexts["left_hand"] = game["STRING_AUTO_SPECTATOR_HIT_HAND"];
+ hitLocTexts["right_arm_upper"] = game["STRING_AUTO_SPECTATOR_HIT_SHOULDER"];
+ hitLocTexts["right_arm_lower"] = game["STRING_AUTO_SPECTATOR_HIT_ARM"];
+ hitLocTexts["right_hand"] = game["STRING_AUTO_SPECTATOR_HIT_HAND"];
+ hitLocTexts["left_leg_upper"] = game["STRING_AUTO_SPECTATOR_HIT_LEG"];
+ hitLocTexts["left_leg_lower"] = game["STRING_AUTO_SPECTATOR_HIT_LEG"];
+ hitLocTexts["left_foot"] = game["STRING_AUTO_SPECTATOR_HIT_FOOT"];
+ hitLocTexts["right_leg_upper"] = game["STRING_AUTO_SPECTATOR_HIT_LEG"];
+ hitLocTexts["right_leg_lower"] = game["STRING_AUTO_SPECTATOR_HIT_LEG"];
+ hitLocTexts["right_foot"] = game["STRING_AUTO_SPECTATOR_HIT_FOOT"];
+
+ self endon("disconnect");
+
+ if (isDefined(self.killcam))
+ return;
+
+ self notify("autoSpectating_showTakenDamageInfo_end");
+ self endon("autoSpectating_showTakenDamageInfo_end");
+
+ if (!isDefined(self.hud_takendamageinfo))
+ {
+ self.hud_takendamageinfo = newClientHudElem2(self);
+ self.hud_takendamageinfo.horzAlign = "center";
+ self.hud_takendamageinfo.vertAlign = "middle";
+ self.hud_takendamageinfo.alignX = "right";
+ self.hud_takendamageinfo.label = game["STRING_AUTO_SPECTATOR_HIT_HP"];
+ self.hud_takendamageinfo.x = -10;
+ self.hud_takendamageinfo.fontscale = 1.1;
+ self.hud_takendamageinfo.archived = false;
+ }
+
+ if (!isDefined(self.hud_takendamageinfo_loc))
+ {
+ self.hud_takendamageinfo_loc = newClientHudElem2(self);
+ self.hud_takendamageinfo_loc.horzAlign = "center";
+ self.hud_takendamageinfo_loc.vertAlign = "middle";
+ self.hud_takendamageinfo_loc.alignX = "left";
+ self.hud_takendamageinfo_loc.x = 2;
+ self.hud_takendamageinfo_loc.fontscale = 1.1;
+ self.hud_takendamageinfo_loc.archived = false;
+ }
+
+ if (iDamage > 100)
+ iDamage = 100;
+
+ self.hud_takendamageinfo setValue(iDamage*-1);
+
+ if (sMeansOfDeath == "MOD_GRENADE_SPLASH")
+ self.hud_takendamageinfo_loc setText(game["STRING_AUTO_SPECTATOR_HIT_GRENADE"]);
+ else if (isDefined(hitLocTexts[sHitLoc]))
+ self.hud_takendamageinfo_loc setText(hitLocTexts[sHitLoc]);
+
+ r = 1;
+ g = 1 - ((iDamage - 50) / 50); if (g < 0) g = 0; if (g > 1) g = 1;
+ b = 1 - (iDamage / 50); if (b < 0) b = 0; if (b > 1) b = 1;
+
+ // red to orange to yellow -> add green
+ // yellow to white -> add blue
+
+ self.hud_takendamageinfo.color = (r, g, b);
+ self.hud_takendamageinfo_loc.color = (1, 1, 1);
+
+ self.hud_takendamageinfo.y = 120;
+ self.hud_takendamageinfo.alpha = 1;
+ self.hud_takendamageinfo_loc.y = 120;
+ self.hud_takendamageinfo_loc.alpha = 1;
+
+ self.hud_takendamageinfo moveovertime(3);
+ self.hud_takendamageinfo.y = 140;
+ self.hud_takendamageinfo_loc moveovertime(3);
+ self.hud_takendamageinfo_loc.y = 140;
+
+ wait level.fps_multiplier * 1;
+
+ self.hud_takendamageinfo FadeOverTime(2);
+ self.hud_takendamageinfo.alpha = 0;
+ self.hud_takendamageinfo_loc FadeOverTime(2);
+ self.hud_takendamageinfo_loc.alpha = 0;
+
+ wait level.fps_multiplier * 2;
+
+ self.hud_takendamageinfo destroy2();
+ self.hud_takendamageinfo_loc destroy2();
+}
+
+
+
+
+
+showKillInfo()
+{
+ self endon("disconnect");
+
+ if (isDefined(self.killcam))
+ return;
+
+ self notify("autoSpectating_showKillInfo_end");
+ self endon("autoSpectating_showKillInfo_end");
+
+ if (!isDefined(self.hud_damageinfo_kill))
+ {
+ self.hud_damageinfo_kill = newClientHudElem2(self);
+ self.hud_damageinfo_kill.horzAlign = "center";
+ self.hud_damageinfo_kill.vertAlign = "middle";
+ self.hud_damageinfo_kill.x = -65;
+ self.hud_damageinfo_kill.archived = false;
+ self.hud_damageinfo_kill setShader("headicon_dead", 23, 23);
+ }
+
+ self.hud_damageinfo_kill.y = 120 - 2;
+ self.hud_damageinfo_kill.alpha = 1;
+ self.hud_damageinfo_kill moveovertime(3);
+ self.hud_damageinfo_kill.y = 100 - 2;
+
+ wait level.fps_multiplier * 1;
+
+ self.hud_damageinfo_kill FadeOverTime(2);
+ self.hud_damageinfo_kill.alpha = 0;
+
+ wait level.fps_multiplier * 2;
+
+ self.hud_damageinfo_kill destroy2();
+}
diff --git a/source/maps/mp/gametypes/_spectating_hud_esp.gsc b/source/maps/mp/gametypes/_spectating_hud_esp.gsc
new file mode 100644
index 0000000..3eb3075
--- /dev/null
+++ b/source/maps/mp/gametypes/_spectating_hud_esp.gsc
@@ -0,0 +1,424 @@
+#include maps\mp\gametypes\global\_global;
+
+init()
+{
+ if (!level.spectatingSystem)
+ return;
+
+ if(game["firstInit"])
+ {
+ precacheShader("stance_stand_front");
+ precacheShader("stance_stand_back");
+ precacheShader("stance_stand_left");
+ precacheShader("stance_stand_right");
+ precacheShader("stance_crouch_front");
+ precacheShader("stance_crouch_back");
+ precacheShader("stance_crouch_left");
+ precacheShader("stance_crouch_right");
+ precacheShader("stance_prone_front");
+ precacheShader("stance_prone_back");
+ precacheShader("stance_prone_left");
+ precacheShader("stance_prone_right");
+ }
+
+ addEventListener("onConnected", ::onConnected);
+}
+
+onConnected()
+{
+ if (!isDefined(self.pers["autoSpectatingESP"]))
+ self.pers["autoSpectatingESP"] = true;
+}
+
+
+
+ESP_Destroy()
+{
+ // Destroy all
+ for(i = 0; i < self.spec_ESP_names.size; i++)
+ {
+ // If HUD is not defined, it means player disconnect and HUD object was deleted
+ if (isDefined(self.spec_ESP_names[i]))
+ self.spec_ESP_names[i] destroy2();
+ if (isDefined(self.spec_ESP_images[i]))
+ self.spec_ESP_images[i] destroy2();
+ }
+ self.spec_ESP_names = undefined;
+ self.spec_ESP_images = undefined;
+
+ if (level.debug_spectator) self iprintln("autospec> ESP HUD destroyed ^1ALL");
+}
+
+/*
+self is spectater
+*/
+ESP_Loop()
+{
+ self endon("disconnect");
+
+ if (isDefined(self.spec_ESP_names))
+ self ESP_Destroy();
+ self.spec_ESP_names = [];
+ self.spec_ESP_images = [];
+
+ self thread FollowCloseByPlayer();
+
+ spectator = self;
+ i_ESP = 0;
+
+ for(;;)
+ {
+ // Clear unused HUD elements
+ for (i = self.spec_ESP_names.size - 1; i >= i_ESP; i--)
+ {
+ // If HUD is not defined, it means player disconnect and HUD object was deleted
+ if (isDefined(self.spec_ESP_names[i]))
+ self.spec_ESP_names[i] destroy2();
+ if (isDefined(self.spec_ESP_images[i]))
+ self.spec_ESP_images[i] destroy2();
+
+ self.spec_ESP_names[i] = undefined;
+ self.spec_ESP_images[i] = undefined;
+
+ if (level.debug_spectator) self iprintln("autospec> ESP HUD destroyed index " + i);
+ }
+
+ wait level.frame;
+
+ i_ESP = 0;
+
+ // Player disconnects
+ if (!isDefined(self))
+ break;
+ // Variables destroyed (not likely)
+ if (!isDefined(self.spec_ESP_names) || !isDefined(self.spec_ESP_images))
+ break;
+ // All needs to be removed (changed team )
+ if (self.pers["team"] != "spectator")
+ break;
+ // Hide hud in Readyup or bash
+ if (level.in_readyup || level.in_bash)
+ continue;
+ // Dont show ESP if killcam or its turned off
+ if (isDefined(spectator.killcam) || !spectator.pers["autoSpectatingESP"])
+ continue;
+
+ // Get spectated player
+ autospectated_player = undefined;
+ if (spectator.pers["autoSpectating"] && isDefined(level.autoSpectating_spectatedPlayer))
+ autospectated_player = level.autoSpectating_spectatedPlayer;
+
+ // Spectating player but not via auto-spectator
+ if (!isDefined(autospectated_player) && !spectator.freeSpectating)
+ continue;
+
+
+ // Find players that will be showed via ESP
+ players = getentarray("player", "classname");
+ for(i = 0; i < players.size; i++)
+ {
+ player = players[i];
+ if (player == spectator || !(player.pers["team"] == "allies" || player.pers["team"] == "axis"))
+ continue;
+ if (player.sessionstate != "playing" && player.sessionstate != "dead")
+ continue;
+
+ // Skip spectated player
+ if (isDefined(autospectated_player) && player == autospectated_player)
+ continue;
+
+ // When auto-spectating (not flying) and followed player died, hide all (because game does not provide correct angles in this session state)
+ if (isDefined(autospectated_player) && autospectated_player.sessionstate != "playing")
+ continue;
+
+ // When auto-spectating show only enemy (except for DM where show all players)
+ // When free-flying show all players
+ if (isDefined(autospectated_player) && autospectated_player.sessionteam == player.sessionteam && player.sessionteam != "none")
+ continue;
+
+
+ // Create new HUD element
+ isNew = false;
+ if (!isDefined(self.spec_ESP_names[i_ESP]))
+ {
+ self.spec_ESP_names[i_ESP] = addHUDClient(self, 10, 10, 1.2, (1,1,1), "center", "bottom", "subleft", "subtop");
+ self.spec_ESP_names[i_ESP].name = "name";
+ self.spec_ESP_names[i_ESP].fontscale = 0.8;
+ self.spec_ESP_names[i_ESP].archived = false;
+
+ self.spec_ESP_images[i_ESP] = addHUDClient(self, 10, 10, 1.2, (1,1,1), "center", "bottom", "subleft", "subtop");
+ self.spec_ESP_images[i_ESP].name = "layout";
+ self.spec_ESP_images[i_ESP].archived = false;
+ self.spec_ESP_images[i_ESP].sort = -1;
+
+ isNew = true;
+
+ if (level.debug_spectator) self iprintln("autospec> ESP HUD added NEW index " + i_ESP);
+ }
+
+ // Update HUD
+ if (isNew || self.spec_ESP_names[i_ESP].player != player)
+ {
+ self.spec_ESP_names[i_ESP].player = player;
+ self.spec_ESP_names[i_ESP].offset = (0, 0, 25);
+ self.spec_ESP_names[i_ESP].tag = "head";
+ self.spec_ESP_names[i_ESP] SetPlayerNameString(player);
+ self.spec_ESP_names[i_ESP] thread SetPlayerWaypoint(self, player);
+ self.spec_ESP_names[i_ESP] thread hud_waypoint_animate(self, player);
+
+ self.spec_ESP_images[i_ESP].player = player;
+ self.spec_ESP_images[i_ESP].offset = (0, 0, -15);
+ self.spec_ESP_images[i_ESP] thread SetPlayerWaypoint(self, player);
+ self.spec_ESP_images[i_ESP] thread hud_waypoint_animate(self, player);
+ }
+
+ i_ESP++;
+ }
+ }
+
+ // Destroy HUD (unles player disconnects)
+ if (isDefined(self))
+ self ESP_Destroy();
+}
+
+
+/*
+When free-flying check for close-by player according to ESP names
+*/
+FollowCloseByPlayer()
+{
+ self endon("disconnect");
+
+ self.spec_follow_text = addHUDClient(self, 0, 50, 1.2, (1,1,1), "center", "top", "center", "top");
+ self.spec_follow_text.alpha = 0;
+
+ saved_player_last = undefined;
+
+ for(;;)
+ {
+ wait level.frame;
+
+ // All needs to be removed (changed team or timeout is called)
+ if (!isDefined(self) || self.pers["team"] != "spectator" || level.in_readyup)
+ break;
+
+ if (!self.freeSpectating)
+ continue;
+
+ saved_dist2D = 0;
+ saved_dist3D = 0;
+ saved_player = undefined;
+
+ for(i = 0; i < self.spec_ESP_names.size; i++)
+ {
+ hud = self.spec_ESP_names[i];
+
+ // If HUD is not defined, it means player disconnect and HUD object was deleted
+ if (!isDefined(hud) || !isDefined(hud.player) || !isAlive(hud.player))
+ continue;
+
+ // Find out looking at player to follow on click
+ dist3D = distance(self.origin, hud.player.origin);
+
+ // If text is somewhere in rectangle around center
+ if ((hud.x > 160 && hud.x < 480 && hud.y > 120 && hud.y < 360) || (dist3D < 300 && hud.x > 160 && hud.x < 480))
+ {
+ if (dist3D < 1000)
+ {
+ dist2D = distance((hud.x, hud.y, 0), (320, 240, 0)); // Distance of text from center - text is in 640x480 rectangle aligned left top
+
+ if (!isDefined(saved_player) || (dist2D < saved_dist2D && dist3D < saved_dist3D))
+ {
+ saved_dist2D = dist2D;
+ saved_dist3D = dist3D;
+ saved_player = hud.player;
+ }
+ }
+ }
+ }
+
+ time = 0.1;
+
+ if (isDefined(saved_player))
+ {
+ if (isDefined(saved_player_last) && saved_player_last != saved_player)
+ {
+ self.spec_follow_text fadeOverTime(time);
+ self.spec_follow_text.alpha = 0;
+ wait level.fps_multiplier * time;
+ if (!isDefined(self)) break; // in case player disconnects
+ }
+ if (!isDefined(saved_player_last) || saved_player_last != saved_player)
+ {
+ self.spec_follow_text SetPlayerNameString(saved_player);
+ self.spec_follow_text fadeOverTime(time);
+ self.spec_follow_text.alpha = 1;
+ wait level.fps_multiplier * time;
+ if (!isDefined(self)) break; // in case player disconnects
+ }
+ }
+ else if (isDefined(saved_player_last))
+ {
+ self.spec_follow_text fadeOverTime(time);
+ self.spec_follow_text.alpha = 0;
+ wait level.fps_multiplier * time;
+ if (!isDefined(self)) break; // in case player disconnects
+ }
+
+ saved_player_last = saved_player;
+
+ self.spec_follow = saved_player;
+ }
+
+ self.spec_follow_text destroy2();
+}
+
+
+
+
+// self is HUD, spectator is hud owner and player is waypoined player
+hud_waypoint_animate(spectator, player)
+{
+ // Make sure only 1 thread is running on this HUD element
+ self notify("hud_waypoint_animate");
+ self endon("hud_waypoint_animate");
+
+ alpha_old = 0;
+ alpha_cnt = 0;
+ alpha_last = 0;
+ self.alpha = 0;
+
+ for (;;)
+ {
+ wait level.frame;
+
+ if (!isDefined(self) || !isDefined(spectator) || !isDefined(player))
+ break;
+
+ // Get spectated player
+ spectated_player = spectator;
+ if (!isDefined(spectator.killcam) && spectator.pers["autoSpectating"] && isDefined(level.autoSpectating_spectatedPlayer))
+ spectated_player = level.autoSpectating_spectatedPlayer;
+
+
+ alpha = 0.0;
+ if (isAlive(player))
+ {
+ // Player is far way
+ dist = distance(spectated_player.origin, player.origin);
+
+ if (self.name == "name")
+ {
+ // Hide name if player is too far
+ if (dist < 1500 || spectator.freeSpectating)
+ alpha = 1;
+ }
+
+ if (self.name == "layout")
+ {
+ // Hide player icon when player is visible
+ if (!spectated_player isPlayerInSight(player))
+ alpha = 0.25;
+ }
+
+ // Hide if mouse is over
+ if (!spectator.freeSpectating)
+ {
+ // If is in center, add alpha
+ dist2D = distance((self.x, self.y, 0), (320, 240, 0)); // Distance of text from center - text is in 640x480 rectangle aligned left top
+ if (dist2D <= 25)
+ alpha = 0.0;
+ else if (dist2D > 25 && dist2D <= 150)
+ alpha = alpha * ((dist2D-25) / 125);
+ }
+ }
+
+ // Alpha smooth animation
+ if (alpha_last != alpha)
+ {
+ alpha_old = self.alpha;
+ alpha_cnt = 10;
+ alpha_last = alpha;
+ }
+ if (alpha_cnt > 0)
+ {
+ self.alpha = alpha_old + ((alpha - alpha_old) / 10) * (10-alpha_cnt);
+ alpha_cnt--;
+ }
+ else
+ self.alpha = alpha;
+
+
+ // Item is not visible, no need to continue animation
+ if (self.alpha == 0)
+ continue;
+
+
+ if (self.name == "name")
+ {
+ color = (0.8, 0.8, 0.8);
+ if (player.pers["team"] == "allies")
+ {
+ if(game["allies"] == "american")
+ color = (0.4, 0.9, .56);
+ else if(game["allies"] == "british")
+ color = (0.45, 0.73, 1);
+ else if(game["allies"] == "russian")
+ color = (1, 0.4, 0.4);
+ }
+ self.color = color;
+ }
+
+
+ if (self.name == "layout")
+ {
+ stance = player maps\mp\gametypes\global\player::getStance(); // prone crouch stand
+
+ angles = player getPlayerAngles();
+
+ diff = (player.origin - spectated_player.origin);
+ diff = (diff[0], diff[1], 0);
+ diff = vectortoangles(diff);
+
+ angle = (angles[1] - diff[1]);
+
+ if (angle > 180) angle -= 360;
+ else if (angle < -180) angle += 360;
+ angle += 180; // flip direction, now in range 0 <-> 360
+ if (angle > 180) angle -= 360; // back to range -180 <-> +180
+
+
+ if ((angle > 135 && angle <= 180) || (angle < -135 && angle >= -180))
+ self.shader = "stance_" + stance + "_back";
+ else if (angle > 45 && angle < 135)
+ self.shader = "stance_" + stance + "_right";
+ else if (angle < -45 && angle > -135)
+ self.shader = "stance_" + stance + "_left";
+ else
+ self.shader = "stance_" + stance + "_front";
+
+ self.w = 25;
+ self.h = 25;
+ self.scale = 1400;
+
+ color = (0.9, 0.9, 0.9);
+ if (player.pers["team"] == "allies")
+ {
+ if(game["allies"] == "american")
+ color = (0.7, 0.95, .8);
+ else if(game["allies"] == "british")
+ color = (0.73, 0.86, 1);
+ else if(game["allies"] == "russian")
+ color = (1, 0.7, 0.7);
+ }
+ if (player.health <= 0)
+ color = (1, 0, 0); // red
+ else if (player.health < 100)
+ {
+ dmg = (player.health / 100);
+ color = (color[0] + (1-color[0]) * (1-dmg), color[1] * dmg, color[2] * dmg);
+ }
+ self.color = color;
+ }
+ }
+}
diff --git a/source/maps/mp/gametypes/_spectating_killcam.gsc b/source/maps/mp/gametypes/_spectating_killcam.gsc
index b5fa1fb..6206bab 100644
--- a/source/maps/mp/gametypes/_spectating_killcam.gsc
+++ b/source/maps/mp/gametypes/_spectating_killcam.gsc
@@ -191,6 +191,8 @@ potencialAutoKillcam(killId)
potencialAutoKillcamForPlayer(killId)
{
+ self endon("disconnect");
+
// Wait untill this player does not see enemy no more - then we can replay killcam
// If player kills another enemy, this record is deleted, this thread ends and new thread is created
time_killer_no_enemy = 0;
@@ -476,11 +478,44 @@ watchIntenseSituation()
openSpectMenu()
{
- self endon("disconnect");
+ //self endon("disconnect");
+
+ /*
+ Open QuickMessage menu trick
+ - this menu is usefull because it does not hide HUD elements as standart menu does
+ - but this menu is openable only via command /mp_qucikmessage in client side
+ - also to open this menu player must be alive player (wich spectator is not)
+ - trick is is move spectator into "none" team and set him as "dead", in wich case the /mp_qucikmessage works
+ - only problem is that this game is quite bugged and there are some side effect:
+ - if players in "allies" or "axis" team are all dead and they are in "spectator" state,
+ for some reason players with clientId lower then spectator clientId starts following this spectator
+ - because spectator is spawned outside map and then imidietly back to spectating mode, players
+ with lower clientId stays outside map
+ - we need to save current position of players with clientId lower then spectator clientId and
+ only if all players are dead in their team
+ */
+ allies = 0;
+ axis = 0;
+ players = getentarray("player", "classname");
+ for(i = 0; i < players.size; i++)
+ { // Count alive players in teams
+ if (players[i].sessionteam == "allies" && players[i].sessionstate != "spectator") allies++;
+ if (players[i].sessionteam == "axis" && players[i].sessionstate != "spectator") axis++;
+ }
+ for(i = 0; i < players.size; i++)
+ { // clientId is lower then spectator and not player are alive in team
+ if (players[i] GetEntityNumber() < self GetEntityNumber() &&
+ ((allies == 0 && players[i].sessionteam == "allies") || (axis == 0 && players[i].sessionteam == "axis")))
+ {
+ players[i].spectating_killcam_origin = players[i].origin;
+ players[i].spectating_killcam_angles = players[i].angles;
+ }
+ }
spectatorclient = self.spectatorclient;
origin = self getOrigin();
angles = self getPlayerAngles();
+
self.sessionteam = "none";
self.sessionstate = "dead"; // enable quickmessage
self.spectatorclient = -1;
@@ -501,16 +536,38 @@ openSpectMenu()
wait level.frame * 2;
- self.sessionteam = "spectator";
- self.sessionstate = "spectator";
- if (self.spectatorclient == -1)
- self.spectatorclient = spectatorclient;
+ // Spectator may disconnect
+ if (isDefined(self))
+ {
+ self.sessionteam = "spectator";
+ self.sessionstate = "spectator";
+ if (self.spectatorclient == -1)
+ self.spectatorclient = spectatorclient;
+
+ wait level.frame;
+ }
- wait level.frame;
+ // Spawn to previous location
+ players = getentarray("player", "classname");
+ for(i = 0; i < players.size; i++)
+ {
+ if (isDefined(players[i].spectating_killcam_origin))
+ {
+ players[i] spawn(players[i].spectating_killcam_origin, players[i].spectating_killcam_angles);
+ players[i].spectating_killcam_origin = undefined;
+ players[i].spectating_killcam_angles = undefined;
+ //players[i] iprintln("^1 APPLIED SPECTATE BUG FIX");
+ }
+ }
- self spawn(origin, angles);
+ // Spawn to previous location
+ if (isDefined(self)) // Spectator may disconnect
+ self spawn(origin, angles);
}
+
+
+
spectMenuSetRows()
{
self.autoSpectating_killIndexes = [];
@@ -572,7 +629,7 @@ replayAction(killId, print)
// Already in killcam, wait for end
while(isDefined(self.killcam))
- wait level.fps_multiplier * 1;
+ wait level.frame;
record = level.autoSpectating_kills[killId];
diff --git a/source/maps/mp/gametypes/_timeout.gsc b/source/maps/mp/gametypes/_timeout.gsc
index 94289ec..5f87e24 100644
--- a/source/maps/mp/gametypes/_timeout.gsc
+++ b/source/maps/mp/gametypes/_timeout.gsc
@@ -34,8 +34,13 @@ init()
game["do_timeout"] = false; // reset request flag
}
+ // Run timeout
+ if (level.in_timeout)
+ {
+ level thread Start_Timeout_Mode(false); // runned_in_middle_of_game = false
+ }
+
// Register notifications catchup
- addEventListener("onStartGameType", ::onStartGameType);
addEventListener("onConnected", ::onConnected);
addEventListener("onJoinedTeam", ::onJoinedTeam);
addEventListener("onSpawned", ::onSpawned);
@@ -56,18 +61,6 @@ onCvarChanged(cvar, value, isRegisterTime)
return false;
}
-// Called after the .gsc::main() and