Merge pull request #68 from Lapikud/development

Ruleset machine translated
pull/74/head
Renkar 3 months ago committed by GitHub
commit b0abaafb01
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 4
      src/app/[locale]/kodukord/page.tsx
  2. 39
      src/app/[locale]/messiala/page.tsx
  3. 20
      src/app/[locale]/reeglid/[slug]/page.tsx
  4. 137
      src/data/rules/en/cs2.md
  5. 29
      src/data/rules/en/kodukord.md
  6. 20
      src/data/rules/en/lol.md
  7. 6
      src/data/rules/et/cs2.md
  8. 0
      src/data/rules/et/kodukord.md
  9. 0
      src/data/rules/et/lol.md
  10. 165
      src/lib/loadRules.ts
  11. 119
      src/lib/rules.ts

@ -3,6 +3,7 @@ import ReactMarkdown, { Components } from "react-markdown";
import { vipnagorgialla } from "@/components/Vipnagorgialla"; import { vipnagorgialla } from "@/components/Vipnagorgialla";
import SectionDivider from "@/components/SectionDivider"; import SectionDivider from "@/components/SectionDivider";
import { getTranslations, setRequestLocale } from "next-intl/server"; import { getTranslations, setRequestLocale } from "next-intl/server";
import { loadRulesBun } from "@/lib/loadRules";
export default async function Page({ export default async function Page({
params, params,
@ -12,8 +13,7 @@ export default async function Page({
const { locale } = await params; const { locale } = await params;
setRequestLocale(locale); setRequestLocale(locale);
const t = await getTranslations({ locale }); const t = await getTranslations({ locale });
const file = Bun.file("src/data/kodukord.md"); const content = await loadRulesBun("kodukord", locale as "et" | "en");
const content = await file.text();
return ( return (
<div> <div>

@ -36,7 +36,6 @@ export default function Expo() {
lvlup: "LVLup!", lvlup: "LVLup!",
redbull: "Red Bull", redbull: "Red Bull",
// fuajee rooms // fuajee rooms
ityk: t("expo.areas.ityk"),
estoniagamedev: t("expo.areas.estoniagamedev"), estoniagamedev: t("expo.areas.estoniagamedev"),
info: t("expo.areas.info"), info: t("expo.areas.info"),
tartuyk: t("expo.areas.tartuyk"), tartuyk: t("expo.areas.tartuyk"),
@ -382,8 +381,8 @@ export default function Expo() {
depth: 3.5, depth: 3.5,
x: -6, x: -6,
z: 2.8, z: 2.8,
color: fuajeeRoomColors[0], color: fuajeeRoomColors[4],
name: roomNames.ityk, name: roomNames.ittk,
}, },
{ {
width: 5, width: 5,
@ -818,37 +817,28 @@ export default function Expo() {
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div <div
className="w-4 h-4 border border-gray-300" className="w-4 h-4 border border-gray-300"
style={{ backgroundColor: "#7b1642" }} style={{ backgroundColor: "#183bbf" }}
></div>
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
{t("expo.areas.ityk")}
</span>
</div>
<div className="flex items-center gap-2">
<div
className="w-4 h-4 border border-gray-300"
style={{ backgroundColor: "#365591" }}
></div> ></div>
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]"> <span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
{t("expo.areas.tartuyk")} {t("expo.areas.estoniagamedev")}
</span> </span>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div <div
className="w-4 h-4 border border-gray-300" className="w-4 h-4 border border-gray-300"
style={{ backgroundColor: "#183bbf" }} style={{ backgroundColor: "#228b22" }}
></div> ></div>
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]"> <span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
{t("expo.areas.estoniagamedev")} {t("expo.areas.gameup")}
</span> </span>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div <div
className="w-4 h-4 border border-gray-300" className="w-4 h-4 border border-gray-300"
style={{ backgroundColor: "#a82838" }} style={{ backgroundColor: "#ff6347" }}
></div> ></div>
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]"> <span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
{t("expo.areas.tly")} {t("expo.areas.info")}
</span> </span>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
@ -863,28 +853,28 @@ export default function Expo() {
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div <div
className="w-4 h-4 border border-gray-300" className="w-4 h-4 border border-gray-300"
style={{ backgroundColor: "#ff6347" }} style={{ backgroundColor: "#20b2aa" }}
></div> ></div>
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]"> <span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
{t("expo.areas.info")} {t("expo.areas.photobooth")}
</span> </span>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div <div
className="w-4 h-4 border border-gray-300" className="w-4 h-4 border border-gray-300"
style={{ backgroundColor: "#228b22" }} style={{ backgroundColor: "#365591" }}
></div> ></div>
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]"> <span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
{t("expo.areas.gameup")} {t("expo.areas.tartuyk")}
</span> </span>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div <div
className="w-4 h-4 border border-gray-300" className="w-4 h-4 border border-gray-300"
style={{ backgroundColor: "#20b2aa" }} style={{ backgroundColor: "#a82838" }}
></div> ></div>
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]"> <span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
{t("expo.areas.photobooth")} {t("expo.areas.tly")}
</span> </span>
</div> </div>
</div> </div>
@ -956,7 +946,6 @@ export default function Expo() {
].includes(hoveredRoom)) || ].includes(hoveredRoom)) ||
(currentView === "fuajee" && (currentView === "fuajee" &&
[ [
roomNames.ityk,
roomNames.tartuyk, roomNames.tartuyk,
roomNames.estoniagamedev, roomNames.estoniagamedev,
roomNames.info, roomNames.info,

@ -3,15 +3,14 @@ import ReactMarkdown, { Components } from "react-markdown";
import { vipnagorgialla } from "@/components/Vipnagorgialla"; import { vipnagorgialla } from "@/components/Vipnagorgialla";
import SectionDivider from "@/components/SectionDivider"; import SectionDivider from "@/components/SectionDivider";
import { getTranslations, setRequestLocale } from "next-intl/server"; import { getTranslations, setRequestLocale } from "next-intl/server";
import { loadRulesBun } from "@/lib/loadRules";
// Map of valid slugs to their corresponding file paths and translation keys // Map of valid slugs to their translation keys
const rulesMap = { const rulesMap = {
lol: { lol: {
filePath: "src/data/rules/lol.md",
titleKey: "rules.lolRules", titleKey: "rules.lolRules",
}, },
cs2: { cs2: {
filePath: "src/data/rules/cs2.md",
titleKey: "rules.cs2Rules", titleKey: "rules.cs2Rules",
}, },
} as const; } as const;
@ -22,7 +21,7 @@ interface PageProps {
params: Promise<{ slug: string; locale: string }>; params: Promise<{ slug: string; locale: string }>;
} }
async function getRuleContent(slug: string) { async function getRuleContent(slug: string, locale: string) {
if (!Object.keys(rulesMap).includes(slug)) { if (!Object.keys(rulesMap).includes(slug)) {
return null; return null;
} }
@ -30,14 +29,19 @@ async function getRuleContent(slug: string) {
const ruleConfig = rulesMap[slug as RuleSlug]; const ruleConfig = rulesMap[slug as RuleSlug];
try { try {
const file = Bun.file(ruleConfig.filePath); const content = await loadRulesBun(
const content = await file.text(); slug as "cs2" | "lol",
locale as "et" | "en",
);
return { return {
content, content,
titleKey: ruleConfig.titleKey, titleKey: ruleConfig.titleKey,
}; };
} catch (error) { } catch (error) {
console.error(`Error reading rule file for slug ${slug}:`, error); console.error(
`Error reading rule file for slug ${slug} in locale ${locale}:`,
error,
);
return null; return null;
} }
} }
@ -46,7 +50,7 @@ export default async function RulePage({ params }: PageProps) {
const { slug, locale } = await params; const { slug, locale } = await params;
setRequestLocale(locale); setRequestLocale(locale);
const t = await getTranslations({ locale }); const t = await getTranslations({ locale });
const ruleData = await getRuleContent(slug); const ruleData = await getRuleContent(slug, locale);
if (!ruleData) { if (!ruleData) {
notFound(); notFound();

@ -0,0 +1,137 @@
## 1. General Information
1. **1.1.** The Counter-Strike 2 (hereinafter CS2) tournament takes place on October 24-26, 2025 at Tallinn University of Technology (TalTech) premises, Ehitajate tee 5, Tallinn.
2. **1.2** The tournament prize pool is 5250€, distributed as follows:
1. **1.2.1** First place team - 600€ per participant
2. **1.2.2** Second place team - 300€ per participant
3. **1.2.3** Third place team - 150€ per participant
3. **1.3** Prize money will be paid to the participant's bank account.
**1.3.1** For underage participants, the prize will be paid to their parent/guardian's bank account.
4. **1.4** Throughout the tournament, all participants must comply with the laws of the Republic of Estonia, TipiLAN house rules, and event regulations.
5. **1.5** By purchasing a ticket, each participant gives permission to be photographed, filmed, and for all photographic, audio, and video material to be used for event documentation and marketing.
6. **1.6** The CS2 main tournament will be recorded and streamed on Twitch and YouTube platforms.
7. **1.7** All tournament-related communications between team members (e.g., in-game chat, voice communications, Discord conversations, etc.) will be recorded.
8. **1.8** When self-streaming the game, the stream delay must be at least 5 minutes.
9. **1.9** Organizers have the right to use participants' personal information solely within the framework of conducting the event.
10. **1.10** The organizing team has the right to modify and edit rules as needed without prior notice.
11. **1.11** The organizing team is impartial towards all participants.
## 2. CS2 Main Tournament Team and Team Composition
1. **2.1** A CS2 main tournament team (hereinafter team) core roster consists of five main members, one of whom is the team captain;
2. **2.2** The team captain is the team's representative who:
1. **2.2.1** Serves as the contact person for the organizing team;
2. **2.2.2** Registers the team for the tournament;
3. **2.2.3** Is responsible for the team's behavior and actions;
4. **2.2.4** Represents the team in cases of warnings, disqualifications, disputes, and timeouts.
3. **2.3** Each team may have one substitute player who is not part of the team's core roster:
1. **2.3.1** The substitute player must purchase a separate substitute player ticket;
2. **2.3.2** The substitute player can replace any member of the team's core roster during the tournament;
3. **2.3.3** The same rights and requirements apply to the substitute player as to the team's core roster.
4. **2.4** Team core roster members may be replaced before the team registration deadline:
**2.4.1** Player replacement is done through Fienta;
**2.4.2** When replacing a member, the team retains the right to a substitute player;
**2.4.3** The same rights and requirements apply to replacement players as to the team's core roster.
5. **2.5** When replacing the team captain (e.g., with a substitute or replacement player), the team decides internally who receives the team captain's rights and responsibilities;
6. **2.6** Teams must confirm their participation, final team core roster, and team name 2 weeks before the tournament in an email sent to the team captain. If a core member drops out after the final roster has been confirmed, the team must use their substitute player.
7. **2.7** If a team withdraws from tournament participation before the registration deadline, the participation fee will be refunded to the team.
8. **2.8** All team members (including core roster, substitute player, replacement player(s)) may only belong to one team at a time and represent only themselves (i.e., it is forbidden to have someone else play on their behalf);
9. **2.9** All team members (including core roster, substitute player, replacement player(s)) must be at least 16 years old by the tournament registration date;
10. **2.10** No team member may be a citizen of the Russian Federation or the Republic of Belarus.
11. **2.11** Teams (including core roster, substitute player, replacement player(s)) are not allowed to:
1. **2.11.1** Use coaches;
2. **2.11.2** Display team sponsors during the tournament;
3. **2.11.3** Play in the interests of another team or team member;
12. **2.12** Team name:
1. **2.12.1** Must not be offensive, vulgar, political, or otherwise inappropriate;
2. **2.12.2** Must not contain emoticons or other symbols that are not letters;
3. **2.12.3** Must be changed upon request from the organizing team.
## 3. Equipment
1. **3.1** The organizing team provides participants with internet, ethernet cable, extension cords, and a seat with a table.
2. **3.2** Tournament participants are responsible for bringing and ensuring the functionality of all other necessary equipment for participation.
## 4. Schedule
1. **4.1** All team members must be present one hour before the scheduled tournament start.
2. **4.2** Teams competing in a match round must be ready at their designated locations 10 minutes before the round begins. It is the team captain's responsibility to ensure their team is in the right place at the right time and ready to start.
3. **4.3** Match round start times are announced by the organizing team at the tournament start or upon completion of the previous match round.
4. **4.4** If a player experiences technical issues with equipment or the game, they must immediately notify the match referee or organizing team.
5. **4.5** The organizing team has the right to make changes to the schedule.
6. **4.6** The organizing team is obligated to keep all participants informed of any delays and changes.
## 5. Game Version and Settings
1. **5.1** The latest version of CS2 will be used throughout the tournament. If the organizing team deems the latest available version unplayable due to bugs or other changes, an older version may be used (if possible).
2. **5.2** The following settings will be used in the CS2 tournament:
1. **5.2.1** Best of 24 (mp_maxrounds 24)
2. **5.2.2** Round time: 1 minute 55 seconds (mp_roundtime 1.92)
3. **5.2.3** Starting money: $800 (mp_startmoney 800)
4. **5.2.4** Freeze time at round start: 20 seconds (mp_freezetime 20)
5. **5.2.5** Buy time: 20 seconds (mp_buytime 20)
6. **5.2.6** Bomb timer: 40 seconds (mp_c4timer 40)
7. **5.2.7** Overtime rounds: best of six (6) (mp_overtime_maxrounds 6)
8. **5.2.8** Overtime starting money: $12,500 (mp_overtime_startmoney 12500)
9. **5.2.9** Round restart delay: 5 seconds (mp_round_restart_delay 5)
10. **5.2.10** Prohibited items: none (mp_items_prohibited "")
3. **5.3** Overtime: if there is a tie after all 24 rounds, overtime will be played as best of six. At the start of each overtime, teams remain on the side they played in the previous half - sides are switched at halftime. Teams continue overtime until a winner is found.
4. **5.4** Pause: each team is allowed to call a timeout for thirty (30) seconds up to three (3) times during regulation rounds. Pauses can be called by participants typing "!pause" in the in-game chat. Players are allowed to use all three pauses consecutively. The match referee can call a pause unilaterally if necessary.
5. **5.5** Technical pause: each team has the right to use a technical pause when necessary. To start a pause, the command ".tech" must be entered in the in-game chat. Technical pauses may only be used for valid reasons, when technical problems occur, and organizers must be immediately notified via Discord after starting the pause.
## 6. Map Selection
1. **6.1** The final tournament format will be announced in October.
2. **6.2** Maps are selected from the currently active Valve Active Duty Map Group.
3. **6.3** Best of 1 (Bo1): coin flip winner decides whether they are Team A or Team B. Team A starts and the process is as follows:
1. **6.3.1** Team A removes two maps.
2. **6.3.2** Team B removes three maps.
3. **6.3.3** Team A removes one map.
4. **6.3.4** The remaining map is played.
4. **6.4** Best of 3 (Bo3): coin flip winner decides whether they are Team A or Team B. Team A starts and the process is as follows:
1. **6.4.1** Team A removes one map.
2. **6.4.2** Team B removes one map.
3. **6.4.3** Team A picks one map.
4. **6.4.4** Team B picks one map.
5. **6.4.5** Team B removes one map.
6. **6.4.6** Team A removes one map.
7. **6.4.7** The remaining map is the decider if needed.
## 7. Prohibited Activities in CS2 Tournament
1. **7.1** Any form of cheating, including methods not mentioned here, is prohibited.
2. **7.2** The use of scripts is prohibited (except for weapon/grenade buying, jump throwing).
3. **7.3** Movement through walls, floors, and ceilings, including sky-walking, is prohibited.
4. **7.4** "Pixel walking" - standing, crouching, walking, and other activities on invisible map boundaries is prohibited.
5. **7.5** Bombs must be placed so they can be defused. This does not include situations where multiple players are needed to defuse the bomb.
6. **7.6** Players are not allowed to place an armed bomb where it cannot be defused, where it doesn't touch a solid object, or where it doesn't make the normal "beeping" sound.
7. **7.7** Players are not allowed to give items names (nametags) that violate what is stated in the TipiLAN house rules.
8. **7.8** Custom game files/data/drivers are not allowed.
9. **7.9** The use of character model skins (agent skins) is not allowed.
10. **7.10** Exploiting in-game bugs is prohibited.
11. **7.11** Any form of match fixing, influencing, fraud, and manipulation is strictly prohibited and means immediate team disqualification. Organizers have the right to notify Estonian law enforcement agencies in case of suspicion.
## 8. Penalties
1. **8.1** Violation of in-game and out-of-game rules (see section 7) and house rules (see TipiLAN house rules) is punishable.
2. **8.2** A team member who violates rules will first receive a first verbal warning. After a second/repeated violation, the team member receives a second verbal warning. On the third occasion, the team member is disqualified from the tournament. The team has the right to use their substitute player.
3. **8.3** A team member who fails to appear for the tournament or match round or leaves during the tournament without valid reason receives a tournament disqualification. The team has the right to use their substitute player.
4. **8.4** A team member who is not present and ready 10 minutes before their match round begins (no-show situation) receives a tournament disqualification, except if the team member is late for valid reasons and has notified the organizing team. The team has the right to use their substitute player.
5. **8.5** If the organizing team determines that a team member has violated section 7, the entire team is immediately disqualified from the tournament. The rule-violating team member receives a permanent ban from TipiLAN tournaments.
6. **8.6** If during match review the organizing team has reasonable suspicion that a team member has violated section 7, the entire team is immediately disqualified from the tournament. The rule-violating team member receives a permanent ban from TipiLAN tournaments.
7. **8.7** Teams have the right to withdraw from tournament participation, i.e., give themselves a disqualification.
8. **8.8** In case of team disqualification, the opposing team automatically wins the current match round.
9. **8.9** Only the team captain can dispute a team member/team disqualification. The dispute must be submitted to the organizing team within 15 minutes of the team being notified of the disqualification.
1. **8.9.1** The organizing team has up to 25 minutes to make a decision regarding the dispute. During this time, the match round is paused and teams may not leave their positions.
10. **8.10** Teams have the right to file a protest in situations where there is a problem that may affect or affects the match round or team:
1. **8.10.1** Protests may be filed with the organizing team within 5 minutes of discovering the problem.
2. **8.10.2** The organizing team has up to 25 minutes to make a decision regarding the protest. During this time, the match round is paused and teams may not leave their positions.
11. **8.11** The match referee notifies the rule-violating team member, their team, and the opposing team of the violation, its content, and consequences.
12. **8.12** The organizing team has the right to pause a match round and end the pause at any time as needed.
13. **8.13** The organizing team is obligated to publicly announce all eliminations and calculations and further changes.

@ -0,0 +1,29 @@
Event participation house rules apply to everyone, both visitors and competitors. In case of violation of house rules, TipiLAN reserves the right to remove the participant from the event and, if necessary, notify the police. In the case of an underage participant, we will notify their parents or guardians in case of serious house rule violations.
# Participant Reminders
1. Upon arrival, exchange your ticket for a wristband.
2. Participants must be at least 16 years old. Ticket controllers may ask you for identification.
3. The event will be photographed and filmed, and event content will be covered in various media channels.
4. If you have TipiLAN-provided accommodation, notify us when exchanging your ticket for a wristband.
5. If you bring your own computer, you will be guided on where to set it up when receiving your wristband.
# Event House Rules
1. Participants are obligated to behave politely and with dignity and respect other event participants.
2. TipiLAN does not tolerate:
2.1. Hate speech based on national, racial, gender, sexual or religious affiliation, disabilities, appearance or age; harassment, threatening, offensive or aggressive behavior, incitement or support thereof
2.2. This applies both on the event premises (IRL) and in online environments related to the event.
3. Participants are obligated to treat the event building, inventory and furnishings with care. It is forbidden to break, stain or move items that do not belong to the participant.
3.1. If a participant has organizer-provided accommodation, then in the accommodation area the participant is obligated to be quiet and allow companions to rest.
3.2. Persons who are not provided accommodation there may not be invited to the accommodation area.
4. TipiLAN is not responsible for participants' personal property.
4.1. The organizer-provided accommodation area is lockable and unauthorized persons are not allowed there, but regardless of this, it is worth keeping an eye on your valuables.
4.2. If there is suspicion that theft has occurred, this must be immediately reported to the organizer.
4.3. When finding lost items, please give them to the organizer or take them to *lost & found* (Merchandise table).
5. Smoking and using vapes is prohibited on the event premises. There are designated smoking areas outside for this purpose.
6. Illegal substances or drugs, sharp objects, firearms, explosives or incendiary materials and other items that may harm participants or others may not be brought to the event.
7. Underage participants are prohibited from consuming alcohol or using nicotine-containing products.
7.1. When purchasing alcohol from the bar, participants are obligated to show identification upon request by bar staff.
8. Participants are obligated to behave responsibly regarding alcohol.
9. Any form of gambling for money or other benefits is prohibited.

@ -0,0 +1,20 @@
## 1. General Information
1. **1.1** The League of Legends (hereinafter LoL) tournament takes place as a two-day event on **October 24-25, 2025** at Tallinn University of Technology (TalTech) premises, Ehitajate tee 5, Tallinn.
2. **1.2** The tournament prize pool is **3500€**, distributed as follows:
1. **1.2.1** First place team – **400€** per participant
2. **1.2.2** Second place team – **200€** per participant
3. **1.2.3** Third place team – **100€** per participant
3. **1.3** Prize money will be paid to the participant's bank account
1. **1.3.1** For underage participants, the prize will be paid to their parent/guardian's bank account
## 2. Teams and Players
1. **2.1** A team must have:
1. **2.1.1** Five members (each member hereinafter referred to individually as *Player*)
2. **2.1.2** One member is the team captain (spokesperson)
3. **2.1.3** All members must be at least 16 years old at the time of registration
4. **2.1.4** The use of coaches during the tournament is not allowed
5. **2.1.5** One registered and physically present member may be substituted
2. **2.2** Players must provide only truthful information about themselves and be prepared to prove their identity to the Organizer.
3. **2.3** Team name and logo as well as player gaming alias and avatar must be appropriate — profanity, vulgarity, political or religious messages, references to alcohol, drugs, or other intoxicants are prohibited.

@ -115,8 +115,8 @@
9. **8.9** Ainult tiimikapten saab tiimiliikme/tiimi diskvalifitseerimist vaidlustada. Vaidlustus tuleb korraldustiimile esitada 15 minuti jooksul alates diskvalifikatsiooni teavitamisest tiimile. 9. **8.9** Ainult tiimikapten saab tiimiliikme/tiimi diskvalifitseerimist vaidlustada. Vaidlustus tuleb korraldustiimile esitada 15 minuti jooksul alates diskvalifikatsiooni teavitamisest tiimile.
1. **8.9.1** Korraldustiimil on aega kuni 25 minutit, et teha otsus vaidlustuse osas. Sellel ajal on mänguvoor pausil ning tiimid ei tohi oma kohtadelt lahkuda. 1. **8.9.1** Korraldustiimil on aega kuni 25 minutit, et teha otsus vaidlustuse osas. Sellel ajal on mänguvoor pausil ning tiimid ei tohi oma kohtadelt lahkuda.
10. **8.10** Tiimidel on õigus esitada protest olukorras, kus esineb probleem, mis võib mõjutada või mõjutab mänguvooru või tiimi: 10. **8.10** Tiimidel on õigus esitada protest olukorras, kus esineb probleem, mis võib mõjutada või mõjutab mänguvooru või tiimi:
**8.10.1** Protesti on õigus esitada korraldustiimile 5 minuti jooksul alates probleemi avastamisest. 1. **8.10.1** Protesti on õigus esitada korraldustiimile 5 minuti jooksul alates probleemi avastamisest.
**8.10.2**Korraldustiimil on aega kuni 25 minutit, et teha otsus protesti osas. Sellel ajal on mänguvoor pausil ning tiimid ei tohi oma kohtadelt lahkuda. 2. **8.10.2** Korraldustiimil on aega kuni 25 minutit, et teha otsus protesti osas. Sellel ajal on mänguvoor pausil ning tiimid ei tohi oma kohtadelt lahkuda.
11. **8.11** Mänguvana teavitab eksimusest, selle sisust ja tagajärjest reegleid rikkunud tiimiliiget, tema tiimi ja vastastiimi. 11. **8.11** Mänguvana teavitab eksimusest, selle sisust ja tagajärjest reegleid rikkunud tiimiliiget, tema tiimi ja vastastiimi.
12. **8.12** Korraldustiimil on õigus panna mänguvoor pausile ja lõpetada paus ükskõik millise hetkel vastavalt vajadusele. 12. **8.12** Korraldustiimil on õigus panna mänguvoor pausile ja lõpetada paus ükskõik millise hetkel vastavalt vajadusele.
Korraldustiimil on kohustus kõikidest väljalangemistest ja -arvamistest ning edasistest muudatustest avalikult teada anda. 13. **8.13** Korraldustiimil on kohustus kõikidest väljalangemistest ja -arvamistest ning edasistest muudatustest avalikult teada anda.

@ -0,0 +1,165 @@
import fs from "fs";
import path from "path";
export type Locale = "et" | "en";
export type RuleType = "cs2" | "lol" | "kodukord";
/**
* Loads any rule content for a specific type and locale
* @param ruleType - The type of rules to load (cs2, lol, kodukord)
* @param locale - The locale to load rules for (et, en)
* @returns Promise<string> The markdown content of the rules file
*/
export async function loadRules(
ruleType: RuleType,
locale: Locale,
): Promise<string> {
// Try to load the file for the current locale first
const filePath = path.join(
process.cwd(),
"src",
"data",
"rules",
locale,
`${ruleType}.md`,
);
try {
// Check if file exists for current locale
if (fs.existsSync(filePath)) {
const file = await import("fs").then(() =>
fs.readFileSync(filePath, "utf8"),
);
return file;
}
// Fallback to Estonian if English version doesn't exist
if (locale !== "et") {
console.warn(
`Rules file not found for ${ruleType} in ${locale}, falling back to Estonian`,
);
const fallbackPath = path.join(
process.cwd(),
"src",
"data",
"rules",
"et",
`${ruleType}.md`,
);
if (fs.existsSync(fallbackPath)) {
const fallbackFile = fs.readFileSync(fallbackPath, "utf8");
return fallbackFile;
}
}
throw new Error(`Rules file not found for ${ruleType}`);
} catch (error) {
console.error(
`Error loading rules for ${ruleType} in locale ${locale}:`,
error,
);
throw new Error(
`Failed to load rules for ${ruleType}: ${error instanceof Error ? error.message : "Unknown error"}`,
);
}
}
/**
* Loads rules using Bun.file (for Bun runtime environments)
* @param ruleType - The type of rules to load
* @param locale - The locale to load rules for
* @returns Promise<string> The markdown content
*/
export async function loadRulesBun(
ruleType: RuleType,
locale: Locale,
): Promise<string> {
// Try to load the file for the current locale first
let filePath = `src/data/rules/${locale}/${ruleType}.md`;
let file = Bun.file(filePath);
// Check if file exists, if not fallback to Estonian
if (!(await file.exists()) && locale !== "et") {
console.warn(
`Rules file not found for ${ruleType} in ${locale}, falling back to Estonian`,
);
filePath = `src/data/rules/et/${ruleType}.md`;
file = Bun.file(filePath);
}
try {
const content = await file.text();
return content;
} catch (error) {
console.error(
`Error loading rules for ${ruleType} in locale ${locale}:`,
error,
);
throw new Error(
`Failed to load rules for ${ruleType}: ${error instanceof Error ? error.message : "Unknown error"}`,
);
}
}
/**
* Gets all available rule types for a given locale
* @param locale - The locale to check for available rules
* @returns Promise<RuleType[]> Array of available rule types
*/
export async function getAvailableRules(locale: Locale): Promise<RuleType[]> {
const rulesDir = path.join(process.cwd(), "src", "data", "rules", locale);
try {
const files = fs.readdirSync(rulesDir);
const ruleTypes = files
.filter((file) => file.endsWith(".md"))
.map((file) => file.replace(".md", "") as RuleType);
return ruleTypes;
} catch (error) {
console.warn(`Could not read rules directory for locale ${locale}:`, error);
return [];
}
}
/**
* Checks if a specific rule file exists for a given locale
* @param ruleType - The type of rules to check
* @param locale - The locale to check
* @returns boolean indicating if the file exists
*/
export function ruleExists(ruleType: RuleType, locale: Locale): boolean {
const filePath = path.join(
process.cwd(),
"src",
"data",
"rules",
locale,
`${ruleType}.md`,
);
return fs.existsSync(filePath);
}
/**
* Gets the best available locale for a rule type
* Prefers the requested locale, falls back to Estonian
* @param ruleType - The type of rules to check
* @param preferredLocale - The preferred locale
* @returns Locale The best available locale for the rule type
*/
export function getBestAvailableLocale(
ruleType: RuleType,
preferredLocale: Locale,
): Locale {
if (ruleExists(ruleType, preferredLocale)) {
return preferredLocale;
}
if (ruleExists(ruleType, "et")) {
return "et";
}
// If neither exists, return preferred (will throw error when trying to load)
return preferredLocale;
}

@ -0,0 +1,119 @@
import fs from "fs";
import path from "path";
export type Locale = "et" | "en";
export type RuleType = "cs2" | "lol" | "kodukord";
/**
* Loads rules content for a specific game and locale
* @param ruleType - The type of rules to load (cs2, lol, kodukord)
* @param locale - The locale to load rules for (et, en)
* @returns The markdown content of the rules file
*/
export async function getRules(
ruleType: RuleType,
locale: Locale,
): Promise<string> {
const filePath = path.join(
process.cwd(),
"src",
"data",
"rules",
locale,
`${ruleType}.md`,
);
try {
const content = fs.readFileSync(filePath, "utf8");
return content;
} catch (error) {
// Fallback to Estonian if English version doesn't exist
if (locale === "en") {
console.warn(
`Rules file not found for ${ruleType} in ${locale}, falling back to Estonian`,
);
const fallbackPath = path.join(
process.cwd(),
"src",
"data",
"rules",
"et",
`${ruleType}.md`,
);
try {
const fallbackContent = fs.readFileSync(fallbackPath, "utf8");
return fallbackContent;
} catch (fallbackError) {
throw new Error(
`Rules file not found for ${ruleType} in either ${locale} or et locale: ${fallbackError}`,
);
}
}
throw new Error(
`Rules file not found for ${ruleType} in ${locale} locale: ${error}`,
);
}
}
/**
* Gets all available rule types
* @param locale - The locale to check for available rules
* @returns Array of available rule types
*/
export async function getAvailableRules(locale: Locale): Promise<RuleType[]> {
const rulesDir = path.join(process.cwd(), "src", "data", "rules", locale);
try {
const files = fs.readdirSync(rulesDir);
const ruleTypes = files
.filter((file) => file.endsWith(".md"))
.map((file) => file.replace(".md", "") as RuleType);
return ruleTypes;
} catch (error) {
console.warn(`Could not read rules directory for locale ${locale}:`, error);
return [];
}
}
/**
* Checks if a specific rule file exists for a given locale
* @param ruleType - The type of rules to check
* @param locale - The locale to check
* @returns Boolean indicating if the file exists
*/
export function ruleExists(ruleType: RuleType, locale: Locale): boolean {
const filePath = path.join(
process.cwd(),
"src",
"data",
"rules",
locale,
`${ruleType}.md`,
);
return fs.existsSync(filePath);
}
/**
* Gets the best available locale for a rule type
* Prefers the requested locale, falls back to Estonian
* @param ruleType - The type of rules to check
* @param preferredLocale - The preferred locale
* @returns The best available locale for the rule type
*/
export function getBestAvailableLocale(
ruleType: RuleType,
preferredLocale: Locale,
): Locale {
if (ruleExists(ruleType, preferredLocale)) {
return preferredLocale;
}
if (ruleExists(ruleType, "et")) {
return "et";
}
// If neither exists, return preferred (will throw error when trying to load)
return preferredLocale;
}
Loading…
Cancel
Save