单败重构

This commit is contained in:
Kunagisa 2025-08-01 02:05:23 +08:00
parent 5aabaddc31
commit 292d913305
12 changed files with 12990 additions and 404 deletions

View File

@ -0,0 +1,545 @@
<?xml version="1.0" encoding="utf-8"?>
<AssetDeclaration xmlns="uri:ea.com:eala:asset" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xai="uri:ea.com:eala:asset:instance">
<Tags></Tags>
<Includes>
<Include type="all" source="DATA:GlobalData/GlobalDefines.xml" />
<Include
type="all"
source="ART:AUAntiAirShip_D.xml" />
<Include
type="all"
source="ART:AUAntiAirShip_FX.w3x" />
<Include
type="all"
source="ART:AUAntiAirShip_FPZ.w3x" />
<Include
type="all"
source="ART:AUAntiAirShip_SKN.w3x" />
<Include
type="all"
source="ART:FXGradient01.xml" />
<Include
type="all"
source="ART:FXTracer.xml" />
<Include
type="all"
source="ART:FXTracerHeroic.xml" />
<!-- needed for temp laserEndParticleSystemFX? -->
<Include
type="all"
source="ART:SUAntiVehicleVehicleTech3_FX.w3x" />
<Include
type="instance"
source="ART:FXGrid_3.xml" />
<Include
type="instance"
source="ART:FXHarpoonBeam.xml" />
<!-- Base Object -->
<Include
type="instance"
source="DATA:BaseObjects/BaseVehicle.xml" />
</Includes>
<!-- aka The Hydrofoil -->
<GameObject
id="AlliedAntiAirShip"
inheritFrom="BaseVehicle"
SelectPortrait="Portrait_AlliedAntiAirShip"
ButtonImage="Button_AlliedAntiAirShip_on"
Side="Allies"
SubGroupPriority="440"
EditorSorting="UNIT"
HealthBoxHeightOffset="30"
TransportSlotCount="10"
BuildTime="10"
CommandSet="AlliedAntiAirShipCommandSet"
KindOf="SELECTABLE CAN_ATTACK CAN_CAST_REFLECTIONS SCORE VEHICLE SHIP CAN_BE_FAVORITE_UNIT"
RadarPriority="UNIT"
ProductionQueueType="WATERCRAFT"
UnitCategory="VEHICLE"
WeaponCategory="CANNON"
VoicePriority="188"
EditorName="AlliedAntiAirShip"
Description="Desc:AlliedAntiAirShip"
TypeDescription="Type:AlliedAntiAirShip"
UnitIntro="Allied_Hydrofoil_UnitIntro">
<DisplayName
xai:joinAction="Replace" xmlns:xai="uri:ea.com:eala:asset:instance">Name:AlliedAntiAirShip</DisplayName>
<ObjectResourceInfo>
<BuildCost Account="=$ACCOUNT_ORE" Amount="900"/>
</ObjectResourceInfo>
<ArmorSet
Armor="AlliedAntiAirShipArmor"
DamageFX="VehicleDamageFX" />
<LocomotorSet
id="DefaultWaterLocomotorSet"
Locomotor="AlliedAntiAirShipWaterLocomotor"
Condition="NORMAL"
Speed="125.0" />
<LocomotorSet
Locomotor="AlliedAntiAirShipWaterLocomotor_LeavingFactory"
Condition="EXITING_PRODUCTION_STRUCTURE"
Speed="125.0" />
<SkirmishAIInformation
UnitBuilderStandardCombatUnit="true" />
<Draws>
<ScriptedModelDraw
id="ModuleTag_Draw"
OkToChangeModelColor="true"
InitialRecoilSpeed="0.1"
MaxRecoilDistance="0.1"
RecoilDamping="2.0"
RecoilSettleSpeed="3.0"
ExtraPublicBone="FX_Weapon_01 FX_Weapon_02" >
<ModelConditionState
ParseCondStateType="PARSE_DEFAULT"
RetainSubObjects="true">
<Model
Name="AUAntiAirShip_SKN" />
<WeaponFireFXBone
WeaponSlotID="1"
WeaponSlotType="PRIMARY_WEAPON"
BoneName="FX_Weapon_01" />
<WeaponRecoilBone
WeaponSlotID="1"
WeaponSlotType="PRIMARY_WEAPON"
BoneName="FX_Weapon_01" />
<WeaponLaunchBone
WeaponSlotID="1"
WeaponSlotType="PRIMARY_WEAPON"
BoneName="FX_Weapon_01" />
<WeaponFireFXBone
WeaponSlotID="1"
WeaponSlotType="SECONDARY_WEAPON"
BoneName="FX_Weapon_02" />
<WeaponRecoilBone
WeaponSlotID="1"
WeaponSlotType="SECONDARY_WEAPON"
BoneName="FX_Weapon_02" />
<WeaponLaunchBone
WeaponSlotID="1"
WeaponSlotType="SECONDARY_WEAPON"
BoneName="FX_Weapon_02" />
<Turret
TurretNameKey="turret"
TurretPitch="Turret_Pitch"
TurretID="1" />
</ModelConditionState>
<ModelConditionState
ParseCondStateType="PARSE_NORMAL"
ConditionsYes="FORMATION_PREVIEW">
<Model
Name="AUAntiAirShip_SKN" />
<Material
ShaderName="FX_FormPreview.fx"
TechniqueName="Default">
<Constants>
<Texture Name="SpecMap">
<Value>FXGradient01</Value>
</Texture>
</Constants>
</Material>
</ModelConditionState>
<ModelConditionState
ParseCondStateType="PARSE_NORMAL"
RetainSubObjects="true"
ConditionsYes="REALLYDAMAGED">
<Model
Name="AUAntiAirShip_SKN" />
<Texture
Original="AUAntiAirShip"
New="AUAntiAirShip_D" />
</ModelConditionState>
<AnimationState
ParseCondStateType="PARSE_DEFAULT">
<Script>
CurDrawableShowSubObjectPermanently("GUN")
CurDrawableHideSubObjectPermanently("BEAM")
</Script>
<ParticleSysBone
BoneName="None"
FXParticleSystemTemplate="SmallShipWakeIdle"
FollowBone="false" />
</AnimationState>
<AnimationState
ParseCondStateType="PARSE_NORMAL"
ConditionsYes="MOVING">
<ParticleSysBone
BoneName="NONE"
FXParticleSystemTemplate="AUHydrofoilWaterWake"
FollowBone="false" />
</AnimationState>
<AnimationState
ParseCondStateType="PARSE_NORMAL"
ConditionsYes="WEAPONSTATE_TWO">
<Script>
CurDrawableHideSubObjectPermanently("GUN")
CurDrawableShowSubObjectPermanently("BEAM")
</Script>
</AnimationState>
</ScriptedModelDraw>
<ScriptedModelDraw
id="ModuleTag_DrawZ"
OkToChangeModelColor="true"
InitialRecoilSpeed="0.1"
MaxRecoilDistance="0.1"
RecoilDamping="2.0"
RecoilSettleSpeed="3.0"
ExtraPublicBone="FX_Weapon_01 FX_Weapon_02">
<ModelConditionState
ParseCondStateType="PARSE_DEFAULT"
RetainSubObjects="true">
<Model
Name="" />
</ModelConditionState>
<ModelConditionState
ParseCondStateType="PARSE_NORMAL"
ConditionsYes="FORMATION_PREVIEW">
<Model
Name="AUAntiAirShip_FPZ" />
</ModelConditionState>
<AnimationState
ParseCondStateType="PARSE_DEFAULT">
<Script>
CurDrawableShowSubObjectPermanently("GUN")
CurDrawableHideSubObjectPermanently("BEAM")
</Script>
</AnimationState>
</ScriptedModelDraw>
<!-- Used in the Weapon Scramble Beam -->
<LaserDraw
id="ModuleTag_LaserDraw"
Texture1_UTile="1"
Texture1_VTile="1"
Texture1_UScrollRate="0"
Texture1_VScrollRate="0"
Texture1_NumFrames="1"
Texture1_FrameRate="30"
Texture2_UTile="1"
Texture2_VTile="1"
Texture2_UScrollRate="0"
Texture2_VScrollRate="1"
Texture2_NumFrames="1"
Texture2_FrameRate="30"
LaserWidth="40"
LaserStateID="1">
<FXShader
ShaderName="Laser.fx"
TechniqueIndex="0">
<Constants>
<Texture
Name="Texture1">
<Value>FXGrid_3</Value>
</Texture>
<Texture
Name="Texture2">
<Value>FXInterlacedMask2</Value>
</Texture>
<Float Name="ColorEmissive">
<Value>0.00000</Value>
<Value>2.00000</Value>
<Value>1.000000</Value>
</Float>
</Constants>
</FXShader>
</LaserDraw>
<!-- Used for the Phalanx Gun -->
<TracerModelDraw
id="ModuleTag_TracerModelDraw"
MinLength="10.0"
MaxLength="25.0"
Width="15.0"
MinSpeed="22"
MaxSpeed="32"
SweepSpeed="3.0"
SpreadAngle="5.0"
MinTracersPerFrame="0.4"
MaxTracersPerFrame="0.4"
FrameLifeTime="25"
WeaponSlotType="PRIMARY_WEAPON"
Texture="FXTracer"
UseAdditiveBlending="true" >
<HeadColor
r="1.0"
g="1.0"
b="1.0"
a="1.0" />
<TailColor
r="1.0"
g="0.75"
b="0.65"
a="0.0" />
<ObjectStatusValidation
ForbiddenStatus="WEAPON_UPGRADED_01 GENERIC_TOGGLE_STATE" />
</TracerModelDraw>
<TracerModelDraw
id="ModuleTag_TracerModelDrawVeterancy"
MinLength="10.0"
MaxLength="25.0"
Width="15.0"
MinSpeed="30"
MaxSpeed="30"
SweepSpeed="0.5"
SpreadAngle="0.5"
MinTracersPerFrame="0.4"
MaxTracersPerFrame="0.4"
FrameLifeTime="35"
WeaponSlotType="PRIMARY_WEAPON"
Texture="FXTracerHeroic"
UseAdditiveBlending="true" >
<HeadColor
r="1.0"
g="0.0"
b="0.0"
a="1.0" />
<TailColor
r="1.0"
g="0.75"
b="0.65"
a="0.0" />
<ObjectStatusValidation
RequiredStatus="WEAPON_UPGRADED_01"
ForbiddenStatus="GENERIC_TOGGLE_STATE" />
</TracerModelDraw>
<!-- DRAW PARTICLES -->
<ScriptedModelDraw
id="ModuleTag_Draw_FX"
OkToChangeModelColor="true">
<ModelConditionState
ParseCondStateType="PARSE_DEFAULT">
<Model
Name="AUAntiAirShip_FX" />
</ModelConditionState>
<ModelConditionState
ParseCondStateType="PARSE_NORMAL"
ConditionsYes="DAMAGED">
<Model
Name="AUAntiAirShip_FX" />
<ParticleSysBone
BoneName="FX_BONE01"
FXParticleSystemTemplate="VehicleDamageSmoke"
FollowBone="true" />
</ModelConditionState>
<ModelConditionState
ParseCondStateType="PARSE_NORMAL"
ConditionsYes="REALLYDAMAGED">
<Model
Name="AUAntiAirShip_FX" />
<ParticleSysBone
BoneName="FX_BONE01"
FXParticleSystemTemplate="VehicleDamageSmoke"
FollowBone="true" />
<ParticleSysBone
BoneName="FX_BONE01"
FXParticleSystemTemplate="VehicleDamageFire"
FollowBone="true" />
<ParticleSysBone
BoneName="FX_BONE01"
FXParticleSystemTemplate="VehicleDamageFire02"
FollowBone="true" />
</ModelConditionState>
</ScriptedModelDraw>
</Draws>
<Behaviors>
<WeaponSetUpdate
id="ModuleTag_WeaponSetUpdate">
<WeaponSlotTurret
ID="1">
<!-- This weapon is always around, but the weapon template itself prevents it
from being able to be fired once it's upgraded. -->
<Weapon
Ordering="PRIMARY_WEAPON"
Template="AlliedAntiAirShipPhalanxGun"
ForbiddenObjectStatus="GENERIC_TOGGLE_STATE"
/>
<Weapon
Ordering="SECONDARY_WEAPON"
Template="AlliedAntiAirShipWeaponScrambler"
ObjectStatus="GENERIC_TOGGLE_STATE"/>
<TurretSettings
TurretTurnRate="360"
MinimumPitch="0d"
AllowsPitch="true"
TurretPitchRate="180"
MinIdleScanTime="1.0s"
MaxIdleScanTime="5.0s"
MinIdleScanAngle="10.0"
MaxIdleScanAngle="90.0"
ComeToHaltJiggle="3d">
<TurretAITargetChooserData
IdleScanDelay="=$FAST_IDLE_SCAN_DELAY"
CanAcquireDynamicIfAssignedOutOfRange="true" />
</TurretSettings>
</WeaponSlotTurret>
</WeaponSetUpdate>
<Physics
id="ModuleTag_Physics" />
<CreateObjectDie
id="ModuleTag_CreateObjectDie"
CreationList="AUAntiAirShip_Die_OCL">
<DieMuxData
DeathTypes="ALL"
DeathTypesForbidden="FLOODED"/>
</CreateObjectDie>
<CreateObjectDie
id="ModuleTag_CreateObjectDieWhole"
CreationList="AUAntiAirShip_Die_OCL">
<DieMuxData
DeathTypes="FLOODED" />
</CreateObjectDie>
<DynamicsUpdate
id="ModuleTag_DefaultDynamicsUpdate"
xai:joinAction="Remove" />
<DestroyDie
id="ModuleTag_Die">
<DieMuxData
DeathTypes="ALL" />
</DestroyDie>
<FXListBehavior
id="ModuleTag_FXList">
<DieMuxData
DeathTypes="ALL" />
<Event
Index="onDeath"
FX="FX_ALL_HydrofoilDie" />
</FXListBehavior>
<!-- The toggle for the Weapon Scrambler -->
<SpecialPower
id="ModuleTag_ActivateWeaponScrambler"
SpecialPowerTemplate="SpecialPower_ToggleWeaponScrambler"
UpdateModuleStartsAttack="true" />
<ToggleStatusSpecialAbilityUpdate
id="ModuleTag_ActivateWeaponScramblerUpdate"
SpecialPowerTemplate="SpecialPower_ToggleWeaponScrambler"
Options="RECONSTITUTE_STORED_COMMAND">
<ToggleState
EnterStateSound="ALL_HydroFoil_ScramblerToggleOffMS">
<SkirmishAiInfo
ToggleHint="TOGGLE_DEFAULT">
<StateWeapon
Weapon="AlliedAntiAirShipPhalanxGun" />
</SkirmishAiInfo>
</ToggleState>
<ToggleState
ObjectStatus="GENERIC_TOGGLE_STATE"
ModelConditions="WEAPONSTATE_TWO"
EnterStateSound="ALL_HydroFoil_ScramblerToggleOnMS">
<SkirmishAiInfo
ToggleHint="TOGGLE_LOCKDOWN"
NeverUseInState="RETREAT">
<StateWeapon
Weapon="AlliedAntiAirShipWeaponScrambler" />
</SkirmishAiInfo>
</ToggleState>
</ToggleStatusSpecialAbilityUpdate>
<!-- The special power that is used by the weapon -->
<SpecialPower
id="ModuleTag_WeaponScrambler"
SpecialPowerTemplate="SpecialPower_WeaponScrambler"
TriggerFX="FX_None"
UpdateModuleStartsAttack="false" />
<LaserState
id="ModuleTag_LaserState"
LaserId="1" >
<LaserEndParticleSystem>AlliedHydroScrambler_Sparks</LaserEndParticleSystem>
<LaserStartParticleSystem>AlliedHydroScrambler_Start</LaserStartParticleSystem>
</LaserState>
<StatusBitsUpgrade
id="ModuleTag_VeterancyUpgrade"
StatusToSet="WEAPON_UPGRADED_01">
<TriggeredBy>Upgrade_Veterancy_HEROIC</TriggeredBy>
</StatusBitsUpgrade>
</Behaviors>
<AI>
<AIUpdate
id="ModuleTag_AI"
AutoAcquireEnemiesWhenIdle="YES"
StateMachine="UnitAIStateMachine">
<UnitAITargetChooserData
CanPickDynamicTargets="false"/>
</AIUpdate>
</AI>
<Body>
<ActiveBody
id="ModuleTag_02"
MaxHealth="400" />
</Body>
<ClientBehaviors>
<ModelConditionAudioLoopClientBehavior id="ModuleTag_ShrunkenVoice">
<ModelConditionSound Sound="ALL_Hydrofoil_VoiceShrunken" RequiredFlags="SHRINK_EFFECT" />
</ModelConditionAudioLoopClientBehavior>
<ModelConditionSoundSelectorClientBehavior id="ModuleTag_VoiceAttackWeaponJammer">
<Override RequiredFlags="WEAPONSTATE_TWO">
<AudioArrayVoice>
<AudioEntry Sound="ALL_Hydrofoil_VoiceAttackSpecial" AudioType="voiceAttack" />
<AudioEntry Sound="ALL_Hydrofoil_VoiceSelectMS" AudioType="voiceSelectBattle" />
</AudioArrayVoice>
</Override>
</ModelConditionSoundSelectorClientBehavior>
<ModelConditionAudioLoopClientBehavior xai:joinAction="Replace" xmlns:xai="uri:ea.com:eala:asset:instance" id="ModuleTag_MagneticSatelliteSuckedAway">
<ModelConditionSound Sound="SOV_MagneticSatellite_SuckedAwayWater" RequiredFlags="SUCKED_UP_HIGH" />
</ModelConditionAudioLoopClientBehavior>
</ClientBehaviors>
<Geometry>
<Shape
Type="BOX"
MajorRadius="30.0"
MinorRadius="18.0"
Height="20.0"
ContactPointGeneration="VEHICLE"/>
</Geometry>
<AudioArrayVoice>
<AudioEntry Sound="ALL_Hydrofoil_VoiceAttack" AudioType="voiceAttack" />
<AudioEntry Sound="ALL_Hydrofoil_VoiceMoveAttack" AudioType="voiceAttackAfterMoving" />
<AudioEntry Sound="ALL_Hydrofoil_VoiceCreate" AudioType="voiceCreated" />
<AudioEntry Sound="ALL_Hydrofoil_VoiceMove" AudioType="voiceMove" />
<AudioEntry Sound="ALL_Hydrofoil_VoiceRetreat" AudioType="voiceRetreatToCastle" />
<AudioEntry Sound="ALL_Hydrofoil_VoiceSelectMS" AudioType="voiceSelect" />
<AudioEntry Sound="ALL_Hydrofoil_VoiceSelectBattleMS" AudioType="voiceSelectBattle" />
<AudioEntry Sound="ALL_Hydrofoil_VoiceSelectUnderFireMS" AudioType="voiceSelectUnderFire" />
<!-- <NamedEntry Sound="ALL_Hydrofoil_VoiceAttackSpecial" Name="VoiceWeaponScrambler" /> oops plays on toggle -->
</AudioArrayVoice>
<AudioArraySound>
<AudioEntry
Sound="ALL_Hydrofoil_MoveStart"
AudioType="soundMoveStart" />
<AudioEntry
Sound="VehicleCrush"
AudioType="soundCrushing" />
<AudioEntry
Sound="Ship_Small_MoveLoopWater"
AudioType="soundMoveLoop" />
</AudioArraySound>
<VisionInfo
VisionRange="325"
ShroudClearingRange="=$STANDARD_SHROUD_CLEAR" />
<CrusherInfo
id="id_CrusherInfo"
CrusherLevel="1"
CrushableLevel="20" />
</GameObject>
</AssetDeclaration>

File diff suppressed because it is too large Load Diff

3253
public/Locomotor.xml Normal file

File diff suppressed because it is too large Load Diff

2291
public/LogicCommand.xml Normal file

File diff suppressed because it is too large Load Diff

1097
public/LogicCommandSet.xml Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -73,7 +73,7 @@ const fetchRankData = async () => {
if (!playerStats[playerName]) {
playerStats[playerName] = {
username: playerName,
faction: player.faction || player.team_name || '',
faction: player.faction || player.team_name || '',
totalWin: 0,
totalLose: 0,
totalPoints: 0,
@ -113,7 +113,7 @@ const fetchRankData = async () => {
bracket_type: 'winners',
score: `${player.totalWin}${player.totalLose}负 (${player.totalPoints.toFixed(1)}分)`,
rounds: player.rounds.join(',')
}))
}))
console.log('RankContestant 最终排名结果:', sortedPlayers)
rankData.value = sortedPlayers

View File

@ -165,16 +165,16 @@ const generateSingleEliminationBracket = async () => {
//
const existingRounds = Object.keys(roundsData).map(Number).sort((a, b) => a - b);
console.log('存在的轮次:', existingRounds);
//
for (const round of existingRounds) {
if (round === 0) {
if (round === 0) {
await generateRound0Matches(roundsData[round] || []);
} else {
} else {
await generateRoundMatches(round, roundsData[round] || []);
}
}
}
//
if (existingRounds.length > 0) {
tournament.value.rounds = Math.max(...existingRounds);
@ -182,131 +182,176 @@ const generateSingleEliminationBracket = async () => {
};
const generateRound0Matches = async (round0Data) => {
const matches = [];
const usedPlayers = new Set();
const matches = [];
const usedPlayers = new Set();
//
for (const player of round0Data) {
if (usedPlayers.has(player.id)) continue;
const rivalName = player.rival_name;
const isBye = rivalName === '轮空' || rivalName === 'bye' || rivalName === 'BYE' || rivalName === 'Bye';
const isPending = rivalName === '待定' || rivalName === 'pending' || rivalName === 'PENDING' || rivalName === 'Pending';
if (usedPlayers.has(player.id)) continue;
const rivalName = player.rival_name;
const isBye = rivalName === '轮空' || rivalName === 'bye' || rivalName === 'BYE' || rivalName === 'Bye';
const isPending = rivalName === '待定' || rivalName === 'pending' || rivalName === 'PENDING' || rivalName === 'Pending';
if (isBye || isPending) continue;
//
//
const rival = round0Data.find(p =>
p.sign_name === rivalName && p.id !== player.id && !usedPlayers.has(p.id)
);
if (rival) {
const matchNumber = matches.length + 1;
matches.push({
p.sign_name === rivalName && p.id !== player.id && !usedPlayers.has(p.id)
);
if (rival) {
const matchNumber = matches.length + 1;
matches.push({
id: `0-${matchNumber}`,
round: 0,
matchNumber: matchNumber,
matchNumber: matchNumber,
participant1: { id: player.id, name: player.sign_name },
participant2: { id: rival.id, name: rival.sign_name },
winner: null,
score1: parseInt(player.win || 0),
score2: parseInt(rival.win || 0),
decided: false
});
usedPlayers.add(player.id);
usedPlayers.add(rival.id);
}
}
winner: null,
score1: parseInt(player.win || 0),
score2: parseInt(rival.win || 0),
decided: false
});
usedPlayers.add(player.id);
usedPlayers.add(rival.id);
}
}
//
for (const player of round0Data) {
if (usedPlayers.has(player.id)) continue;
const rivalName = player.rival_name;
const isBye = rivalName === '轮空' || rivalName === 'bye' || rivalName === 'BYE' || rivalName === 'Bye';
if (isBye) {
const matchNumber = matches.length + 1;
matches.push({
if (usedPlayers.has(player.id)) continue;
const rivalName = player.rival_name;
const isBye = rivalName === '轮空' || rivalName === 'bye' || rivalName === 'BYE' || rivalName === 'Bye';
if (isBye) {
const matchNumber = matches.length + 1;
matches.push({
id: `0-${matchNumber}`,
round: 0,
matchNumber: matchNumber,
matchNumber: matchNumber,
participant1: { id: player.id, name: player.sign_name },
participant2: null,
participant2: null,
winner: { id: player.id, name: player.sign_name },
score1: 1,
score2: 0,
decided: true
});
usedPlayers.add(player.id);
}
}
tournament.value.matches.push(...matches);
score1: 1,
score2: 0,
decided: true
});
usedPlayers.add(player.id);
}
}
tournament.value.matches.push(...matches);
};
const generateRoundMatches = async (round, roundData) => {
const matches = [];
const usedPlayers = new Set();
const matches = [];
const usedPlayers = new Set();
console.log(`生成第${round}轮比赛,数据:`, roundData);
console.log(`${round}轮详细数据:`, roundData.map(p => ({
id: p.id,
sign_name: p.sign_name,
rival_name: p.rival_name,
status: p.status,
win: p.win,
lose: p.lose
})));
//
for (const player of roundData) {
if (usedPlayers.has(player.id)) continue;
const rivalName = player.rival_name;
const isBye = rivalName === '轮空' || rivalName === 'bye' || rivalName === 'BYE' || rivalName === 'Bye';
const isPending = rivalName === '待定' || rivalName === 'pending' || rivalName === 'PENDING' || rivalName === 'Pending';
if (isBye || isPending) continue;
//
const rival = roundData.find(p =>
p.sign_name === rivalName && p.id !== player.id && !usedPlayers.has(p.id)
);
if (rival) {
const matchNumber = matches.length + 1;
matches.push({
id: `${round}-${matchNumber}`,
round: round,
matchNumber: matchNumber,
participant1: { id: player.id, name: player.sign_name },
participant2: { id: rival.id, name: rival.sign_name },
winner: null,
score1: parseInt(player.win || 0),
score2: parseInt(rival.win || 0),
decided: false
});
usedPlayers.add(player.id);
usedPlayers.add(rival.id);
if (usedPlayers.has(player.id)) {
console.log(`玩家 ${player.sign_name} (ID: ${player.id}) 已被使用,跳过`);
continue;
}
}
const rivalName = player.rival_name;
const isBye = rivalName === '轮空' || rivalName === 'bye' || rivalName === 'BYE' || rivalName === 'Bye';
const isPending = rivalName === '待定' || rivalName === 'pending' || rivalName === 'PENDING' || rivalName === 'Pending';
if (isBye || isPending) {
console.log(`玩家 ${player.sign_name} 的对手是 ${rivalName},跳过正常比赛处理`);
continue;
}
//
const rival = roundData.find(p =>
p.sign_name === rivalName && p.id !== player.id && !usedPlayers.has(p.id)
);
if (rival) {
const matchNumber = matches.length + 1;
//
const playerStatus = player.status;
const rivalStatus = rival.status;
const playerWin = parseInt(player.win || 0);
const rivalWin = parseInt(rival.win || 0);
//
const isDecided = playerWin > 0 || rivalWin > 0 ||
playerStatus === 'win' || playerStatus === 'lose' ||
rivalStatus === 'win' || rivalStatus === 'lose';
//
let winner = null;
if (playerStatus === 'win' || playerWin > rivalWin) {
winner = { id: player.id, name: player.sign_name };
} else if (rivalStatus === 'win' || rivalWin > playerWin) {
winner = { id: rival.id, name: rival.sign_name };
}
console.log(`创建比赛 ${matchNumber}: ${player.sign_name} vs ${rival.sign_name}`, {
playerStatus,
rivalStatus,
isDecided,
winner: winner?.name,
playerData: { id: player.id, name: player.sign_name, status: player.status, win: player.win, lose: player.lose },
rivalData: { id: rival.id, name: rival.sign_name, status: rival.status, win: rival.win, lose: rival.lose }
});
matches.push({
id: `${round}-${matchNumber}`,
round: round,
matchNumber: matchNumber,
participant1: { id: player.id, name: player.sign_name },
participant2: { id: rival.id, name: rival.sign_name },
winner: winner,
score1: parseInt(player.win || 0),
score2: parseInt(rival.win || 0),
decided: isDecided
});
usedPlayers.add(player.id);
usedPlayers.add(rival.id);
} else {
console.log(`未找到玩家 ${player.sign_name} 的对手 ${rivalName}`);
}
}
//
for (const player of roundData) {
if (usedPlayers.has(player.id)) continue;
const rivalName = player.rival_name;
const isBye = rivalName === '轮空' || rivalName === 'bye' || rivalName === 'BYE' || rivalName === 'Bye';
if (isBye) {
const matchNumber = matches.length + 1;
matches.push({
id: `${round}-${matchNumber}`,
round: round,
matchNumber: matchNumber,
participant1: { id: player.id, name: player.sign_name },
participant2: null,
winner: { id: player.id, name: player.sign_name },
score1: 1,
score2: 0,
decided: true
});
usedPlayers.add(player.id);
}
}
const rivalName = player.rival_name;
const isBye = rivalName === '轮空' || rivalName === 'bye' || rivalName === 'BYE' || rivalName === 'Bye';
if (isBye) {
const matchNumber = matches.length + 1;
matches.push({
id: `${round}-${matchNumber}`,
round: round,
matchNumber: matchNumber,
participant1: { id: player.id, name: player.sign_name },
participant2: null,
winner: { id: player.id, name: player.sign_name },
score1: 1,
score2: 0,
decided: true
});
usedPlayers.add(player.id);
}
}
//
for (const player of roundData) {
@ -317,43 +362,84 @@ const generateRoundMatches = async (round, roundData) => {
if (isPending) {
const matchNumber = matches.length + 1;
// winlose
const playerWin = parseInt(player.win || 0);
const isDecided = player.status === 'win' || player.status === 'lose' || playerWin > 0;
let winner = null;
if (player.status === 'win' || playerWin > 0) {
winner = { id: player.id, name: player.sign_name };
}
console.log(`创建待定比赛 ${matchNumber}: ${player.sign_name} vs 待定`, {
playerStatus: player.status,
isDecided,
winner: winner?.name
});
matches.push({
id: `${round}-${matchNumber}`,
round: round,
matchNumber: matchNumber,
participant1: { id: player.id, name: player.sign_name },
participant2: null,
winner: null,
winner: winner,
score1: parseInt(player.win || 0),
score2: 0,
decided: false
decided: isDecided
});
usedPlayers.add(player.id);
}
}
console.log(`${round}轮生成比赛:`, matches);
tournament.value.matches.push(...matches);
tournament.value.matches.push(...matches);
};
//
const isMatchOrderValid = (match) => {
console.log('检查比赛顺序:', {
matchId: match.id,
round: match.round,
matchNumber: match.matchNumber
});
const currentRoundMatches = tournament.value.matches
.filter(m => m.round === match.round)
.sort((a, b) => a.matchNumber - b.matchNumber);
console.log('当前轮次所有比赛:', currentRoundMatches.map(m => ({
id: m.id,
matchNumber: m.matchNumber,
decided: m.decided,
participant1: m.participant1?.name,
participant2: m.participant2?.name,
winner: m.winner?.name,
score1: m.score1,
score2: m.score2
})));
const currentMatchIndex = currentRoundMatches.findIndex(m => m.id === match.id);
console.log('当前比赛索引:', currentMatchIndex);
//
for (let i = 0; i < currentMatchIndex; i++) {
const previousMatch = currentRoundMatches[i];
console.log(`检查第${i + 1}场比赛:`, {
id: previousMatch.id,
matchNumber: previousMatch.matchNumber,
decided: previousMatch.decided
});
if (!previousMatch.decided) {
console.log(`${previousMatch.matchNumber}场比赛未完成,阻止进行第${match.matchNumber}场比赛`);
return false;
}
}
console.log('比赛顺序验证通过');
return true;
};
@ -474,6 +560,14 @@ const handlePlayerAdvancement = async (match) => {
};
const confirmScore = async (match) => {
console.log('确认比分,比赛信息:', {
id: match.id,
round: match.round,
matchNumber: match.matchNumber,
participant1: match.participant1?.name,
participant2: match.participant2?.name
});
//
if (!isMatchOrderValid(match)) {
const currentRoundMatches = tournament.value.matches
@ -483,6 +577,12 @@ const confirmScore = async (match) => {
const currentMatchIndex = currentRoundMatches.findIndex(m => m.id === match.id);
const previousMatch = currentRoundMatches[currentMatchIndex - 1];
console.log('比赛顺序验证失败,阻止的比赛:', {
currentMatch: match.matchNumber,
previousMatch: previousMatch?.matchNumber,
previousMatchId: previousMatch?.id
});
errorDialog.value = {
visible: true,
message: `请先完成第${previousMatch.matchNumber}场比赛`
@ -531,16 +631,16 @@ const confirmScore = async (match) => {
await addSignUpResult(newRecord);
console.log(`轮空玩家 ${p1Data.sign_name} 晋级到第${nextRound}`);
}
} catch (error) {
} catch (error) {
console.error('处理轮空晋级失败:', error);
}
}
nextTick(() => drawD3Bracket());
await loadTournamentData();
emit('refreshPlayers');
return;
}
}
//
const s1 = Number(match.score1);
const s2 = Number(match.score2);
@ -670,9 +770,9 @@ const drawD3Bracket = () => {
const roundMatches = tournament.value.matches
.filter(m => m.round === round)
.sort((a, b) => a.matchNumber - b.matchNumber);
console.log(`绘制第${round}轮比赛:`, roundMatches);
const matchesInRound = roundMatches.length;
const roundX = round * (matchWidth + roundGap);
const roundStartY = (maxMatchesInRound * (matchHeight + matchGap) - matchesInRound * (matchHeight + matchGap)) / 2;

View File

@ -49,13 +49,15 @@ const routes = [
path: 'weapon-match',
name: 'WeaponMatch',
component: () => import('@/views/weapon/WeaponMatch.vue'),
meta: { requiresAuth: true, requiredPrivilege: ['lv-admin','lv-mod'] }
// meta: { requiresAuth: true, requiredPrivilege: ['lv-admin','lv-mod'] }
meta: { requiresAuth: true}
},
{
path: 'configEditor',
name: 'ConfigEditor',
component: () => import('@/views/index/ConfigEditor.vue'),
meta: { requiresAuth: true, requiredPrivilege: ['lv-admin','lv-mod'] }
// meta: { requiresAuth: true, requiredPrivilege: ['lv-admin','lv-mod'] }
meta: { requiresAuth: true}
},
{
path: 'competition',
@ -102,13 +104,13 @@ const routes = [
path: 'PIC2TGA',
name: 'PIC2TGA',
component: () => import('@/views/index/PIC2TGA.vue'),
meta: { requiredPrivilege: ['lv-admin','lv-mod','lv-map','lv-competitor'] }
// meta: { requiredPrivilege: ['lv-admin','lv-mod','lv-map','lv-competitor'] }
},
{
path: 'terrainGenerate',
name: 'TerrainGenerate',
component: () => import('@/views/index/TerrainGenerate.vue'),
meta: { requiredPrivilege: ['lv-admin','lv-mod','lv-map','lv-competitor'] }
// meta: { requiredPrivilege: ['lv-admin','lv-mod','lv-map','lv-competitor'] }
}
]
},

View File

@ -437,12 +437,12 @@ function handlePasswordChangeError(errorMessage) {
:message="errorDialogMessage"
@close="errorDialogVisible = false"
/>
<PrivilegeRequestDialog
:visible="privilegeDialogVisible"
:privilegeName="privilegeDialogName"
@close="privilegeDialogVisible = false"
@apply="handlePrivilegeApply"
/>
<!-- <PrivilegeRequestDialog-->
<!-- :visible="privilegeDialogVisible"-->
<!-- :privilegeName="privilegeDialogName"-->
<!-- @close="privilegeDialogVisible = false"-->
<!-- @apply="handlePrivilegeApply"-->
<!-- />-->
<SuccessDialog
:visible="successDialog.visible"
:message="successDialog.message"

File diff suppressed because it is too large Load Diff