openvibers/source.openvibe.games Commits
Clean OpenVibe startup and add hardening APIs
Changes
@@@ -17,8 +17,11 @@ game/openvibe.games/demoheader.tmpgame/openvibe.games/download/game/openvibe.games/downloadlists/game/openvibe.games/logs/--game/openvibe.games/materials/vgui/++game/openvibe.games/materials/vgui/*++!game/openvibe.games/materials/vgui/gfx/++!game/openvibe.games/materials/vgui/gfx/**game/openvibe.games/cfg/settings.scr++backups/*.corecore@@@ -8,14 +8,14 @@- Backend API starts and responds on `http://127.0.0.1:3000`.- PostgreSQL dev database starts through the repo tooling.- Five SRCDS shards boot:-- - Hub: `ov_hub` on `27015`-- - Prop Hunt: `ph_openvibe_dev` on `27016`-- - Deathrun: `dr_openvibe_dev` on `27017`-- - Fort Wars: `fw_openvibe_dev` on `27018`-- - Traitor Town: `tt_openvibe_dev` on `27019`++ - Hub: `ov_hub` on `127.0.0.1:27015`++ - Prop Hunt: `ph_openvibe_dev` on `127.0.0.2:27016`++ - Deathrun: `dr_openvibe_dev` on `127.0.0.3:27017`++ - Fort Wars: `fw_openvibe_dev` on `127.0.0.4:27018`++ - Traitor Town: `tt_openvibe_dev` on `127.0.0.5:27019`- Sidecars register and heartbeat those servers with the backend.- Travel requests return live local connect targets for every mode.--- Maps use `ov_join <mode>` portal commands instead of hardcoded connects.++- Maps use direct local connect commands for the current Proton client fallback; regenerate with `OPENVIBE_USE_OV_JOIN=1` after rebuilding/loading the custom OpenVibe client DLL.## Implemented Pieces@@@ -32,6 +32,9 @@| Steam auth | Implemented route | Requires real `STEAM_WEB_API_KEY` and `STEAM_APP_ID` for production. || CDN manifest | Implemented route | Requires real hosted storage under `openvibe.games` for production content. || Production infra | Scaffolded | Docker Compose, Docker Swarm, Kubernetes, nginx CDN config. |++| Parties/friend invites | Implemented | Create party, invite, accept, capacity-aware party travel. |++| Moderation/audit | Implemented | Admin audit event write/list routes. |++| Metrics/backups | Implemented | `/metrics` endpoint and `tools/backup-postgres.sh`. |## Verified@@@ -41,7 +44,7 @@ npm run buildnpm test```--Result: TypeScript build passed, 9 tests passed.++Result: TypeScript build passed, 11 tests passed.```bashcd ~/src/openvibe-source@@@ -81,6 +84,6 @@ Before public deployment:- Configure real Steam app identity and `STEAM_WEB_API_KEY`.- Host cosmetic assets under the `openvibe.games` CDN path.- Set real production secrets for API, Redis, PostgreSQL, and admin routes.--- Add backups, metrics, health checks, log shipping, and deployment secret management.++- Add alert routing, log shipping, deployment secret management, and regular restore drills.- Expand each game mode from prototype rules into full production gameplay.- Add moderation/admin UI and anti-abuse controls.@@@ -46,13 +46,13 @@ openvibe-source/## Game Modes--| Mode | Map | Port | Join command |--| --- | --- | ---: | --- |--| Hub | `ov_hub` | 27015 | `ov_join hub` |--| Prop Hunt | `ph_openvibe_dev` | 27016 | `ov_join prophunt` |--| Deathrun | `dr_openvibe_dev` | 27017 | `ov_join deathrun` |--| Fort Wars | `fw_openvibe_dev` | 27018 | `ov_join fortwars` |--| Traitor Town | `tt_openvibe_dev` | 27019 | `ov_join traitortown` |++| Mode | Map | Local target | Authenticated command |++| --- | --- | --- | --- |++| Hub | `ov_hub` | `127.0.0.1:27015` | `ov_join hub` |++| Prop Hunt | `ph_openvibe_dev` | `127.0.0.2:27016` | `ov_join prophunt` |++| Deathrun | `dr_openvibe_dev` | `127.0.0.3:27017` | `ov_join deathrun` |++| Fort Wars | `fw_openvibe_dev` | `127.0.0.4:27018` | `ov_join fortwars` |++| Traitor Town | `tt_openvibe_dev` | `127.0.0.5:27019` | `ov_join traitortown` |## Build And Test@@@ -91,7 +91,7 @@ tools/dev-down.sh## Verified On June 28, 2026- Backend TypeScript build passed.--- Backend Vitest suite passed: 9 tests.++- Backend Vitest suite passed: 11 tests.- Source SDK 2013 Linux64 build passed with OpenVibe C++ patch applied.- SRCDS smoke test passed for all five maps.- Full dev stack registered all five servers and returned valid travel targets for every mode.@@@ -67,16 +67,18 @@ node tools/generate-dev-vmfs.mjstools/compile-dev-maps-wine.sh```--Portal pads currently emit:++Portal pads currently emit direct local connects for the Proton fallback:```text--ov_join hub--ov_join prophunt--ov_join deathrun--ov_join fortwars--ov_join traitortown++connect 127.0.0.1:27015++connect 127.0.0.2:27016++connect 127.0.0.3:27017++connect 127.0.0.4:27018++connect 127.0.0.5:27019```++Set `OPENVIBE_USE_OV_JOIN=1` before regenerating VMFs to bake authenticated `ov_join <mode>` portal commands for a rebuilt custom client DLL.++## Client / Server Commands| Command | Side | Purpose |@@@ -95,6 +97,7 @@ ov_join traitortowncurl -s http://127.0.0.1:3000/health | jqcurl -s http://127.0.0.1:3000/v1/servers | jqcurl -s http://127.0.0.1:3000/v1/assets/manifest | jq++curl -s http://127.0.0.1:3000/metrics```Travel request:@@@ -128,6 +131,13 @@ OPENVIBE_ADMIN_SECRETOPENVIBE_CDN_BASE_URL```++## Backup++++```bash++cd ~/src/openvibe-source++tools/backup-postgres.sh++```++## Common Fixes| Issue | Fix |@@@ -24,14 +24,15 @@ Implemented and tested locally:- **Prop Hunt C++ disguise command** - `ov_prophunt_disguise <model>` swaps the player to an allowlisted prop model; `ov_prophunt_reset_disguise` restores the player model.- **Fort Wars C++ placement command** - `ov_fortwars_spawn <crate|barrel|fence|plate|concrete>` spawns allowlisted physics props during the build phase.- **VScript game logic** - Squirrel scripts for hub, Prop Hunt, Deathrun, Fort Wars, and Traitor Town prototypes.--- **Portal maps** - generated VMFs and compiled BSPs use `ov_join` portals for hub/minigame travel.++- **Portal maps** - generated VMFs and compiled BSPs use direct local connects for the current Proton fallback; set `OPENVIBE_USE_OV_JOIN=1` when generating maps for a rebuilt custom client DLL.- **Sidecars** - tail SRCDS logs and bridge `[OV]` events to the backend API.- **Production infra skeleton** - Docker Compose, Docker Swarm stack, and Kubernetes manifests for API, PostgreSQL, Redis, and CDN/static assets.++- **Hardening routes** - party invites, capacity-aware party travel, moderation/audit events, Prometheus-style metrics, and backup tooling.Verified locally on June 28, 2026:- `backend`: TypeScript build passes.--- `backend`: 9 Vitest tests pass.++- `backend`: 11 Vitest tests pass.- Source SDK 2013 Linux64 build passes with the OpenVibe C++ patch applied.- SRCDS smoke test passes for all five maps: `ov_hub`, `ph_openvibe_dev`, `dr_openvibe_dev`, `fw_openvibe_dev`, `tt_openvibe_dev`.- Full dev stack registers all five servers and returns travel targets for hub, Prop Hunt, Deathrun, Fort Wars, and Traitor Town.@@@ -154,7 +155,7 @@ Source client-> Steam auth ticket or dev auth-> OpenVibe.Games API-> Hub SRCDS-- -> ov_join portal pads++ -> portal pads-> Minigame SRCDS shardsOpenVibe.Games API@@@ -165,6 +166,8 @@ OpenVibe.Games APIGameplay remains Source-style authoritative SRCDS instances. The MMO-style part is the shared OpenVibe.Games account, inventory, currency, travel, and server orchestration layer.++Local Proton builds use direct portal connects so the maps work even when the Windows client DLL has not been rebuilt. The authenticated C++ travel path is still available by regenerating maps with `OPENVIBE_USE_OV_JOIN=1`.++## API Endpoints| Method | Path | Description |@@@ -183,8 +186,16 @@ Gameplay remains Source-style authoritative SRCDS instances. The MMO-style part| `GET` | `/v1/servers` | List live servers || `POST` | `/v1/travel/request` | Reserve travel token || `POST` | `/v1/travel/validate` | Consume travel token |++| `POST` | `/v1/parties` | Create party |++| `GET` | `/v1/parties?partyId=` | Get party members |++| `POST` | `/v1/parties/invite` | Invite friend to party |++| `POST` | `/v1/parties/invite/accept` | Accept party invite |++| `POST` | `/v1/parties/travel` | Reserve one server for the whole party || `POST` | `/v1/matches/end` | Record match result + reward || `POST` | `/v1/admin/shop/items` | Admin: upsert shop item |++| `POST` | `/v1/admin/audit/events` | Admin: record moderation/audit event |++| `GET` | `/v1/admin/audit/events` | Admin: list audit events |++| `GET` | `/metrics` | Prometheus-style service metrics |## Production Configuration@@@ -204,6 +215,13 @@ Infrastructure templates live in:- `infra/kubernetes/openvibe.yaml`- `infra/cdn/nginx.conf`++Local database backups:++++```bash++cd ~/src/openvibe-source++tools/backup-postgres.sh++```++## Remaining WorkThe foundation is running. The next real work is content and hardening:@@@ -211,6 +229,5 @@ The foundation is running. The next real work is content and hardening:- Replace dev models/trails with real OpenVibe cosmetic assets.- Add real hosted CDN storage behind `openvibe.games`.- Wire Steam production credentials and app identity.--- Add party travel, friend invites, and reservation capacity checks.- Expand Prop Hunt, Deathrun, Fort Wars, and Traitor Town into full game rules.--- Add admin moderation, audit views, metrics, backups, and deployment secrets management.++- Add full admin web views, alert routing, and deployment secrets management.@@@ -75,6 +75,38 @@ CREATE TABLE IF NOT EXISTS match_results (UNIQUE (match_id, steam_id));++CREATE TABLE IF NOT EXISTS parties (++ party_id TEXT PRIMARY KEY,++ leader_steam_id BIGINT NOT NULL REFERENCES players(steam_id) ON DELETE CASCADE,++ created_at TIMESTAMPTZ NOT NULL DEFAULT now()++);++++CREATE TABLE IF NOT EXISTS party_members (++ party_id TEXT NOT NULL REFERENCES parties(party_id) ON DELETE CASCADE,++ steam_id BIGINT NOT NULL REFERENCES players(steam_id) ON DELETE CASCADE,++ joined_at TIMESTAMPTZ NOT NULL DEFAULT now(),++ PRIMARY KEY (party_id, steam_id)++);++++CREATE TABLE IF NOT EXISTS party_invites (++ invite_id TEXT PRIMARY KEY,++ party_id TEXT NOT NULL REFERENCES parties(party_id) ON DELETE CASCADE,++ invited_by_steam_id BIGINT NOT NULL REFERENCES players(steam_id) ON DELETE CASCADE,++ invited_steam_id BIGINT NOT NULL REFERENCES players(steam_id) ON DELETE CASCADE,++ status TEXT NOT NULL CHECK (status IN ('pending','accepted','declined','expired')),++ expires_at TIMESTAMPTZ NOT NULL,++ created_at TIMESTAMPTZ NOT NULL DEFAULT now()++);++++CREATE TABLE IF NOT EXISTS audit_events (++ audit_id TEXT PRIMARY KEY,++ actor_steam_id BIGINT NOT NULL,++ action TEXT NOT NULL,++ target_steam_id BIGINT,++ reason TEXT NOT NULL,++ created_at TIMESTAMPTZ NOT NULL DEFAULT now()++);++CREATE INDEX IF NOT EXISTS idx_game_servers_openON game_servers (mode, state, player_count, last_heartbeat);@@@ -82,6 +114,12 @@ CREATE INDEX IF NOT EXISTS idx_join_tokens_validON join_tokens (steam_id, server_id, expires_at)WHERE consumed_at IS NULL;++CREATE INDEX IF NOT EXISTS idx_party_invites_invited++ON party_invites (invited_steam_id, status, expires_at);++++CREATE INDEX IF NOT EXISTS idx_audit_events_created++ON audit_events (created_at DESC);++INSERT INTO shop_items (item_id, item_type, display_name, description, asset_path, price)VALUES('model_rebel', 'player_model', 'Rebel', 'Baseline resistance player model.', 'models/player/group03/male_07.mdl', 0),@@@ -311,4 +311,88 @@ describe("OpenVibe API vertical slice", () => {expect(res.statusCode).toBe(403);await app.close();});++++ it("supports party invites and capacity-aware party travel", async () => {++ const app = await testApp();++ const friendSteamId = "76561198000000003";++++ await app.inject({ method: "POST", url: "/v1/auth/dev",++ payload: { steamId, displayName: "Party Leader" } });++ await app.inject({ method: "POST", url: "/v1/auth/dev",++ payload: { steamId: friendSteamId, displayName: "Party Friend" } });++++ await app.inject({ method: "POST", url: "/v1/servers/register",++ payload: { serverId: "party-prophunt-1", serverSecret, mode: "prophunt",++ mapName: "ph_openvibe_dev", publicHost: "127.0.0.1", port: 27016, maxPlayers: 1 } });++ await app.inject({ method: "POST", url: "/v1/servers/register",++ payload: { serverId: "party-prophunt-2", serverSecret, mode: "prophunt",++ mapName: "ph_openvibe_dev", publicHost: "127.0.0.1", port: 27026, maxPlayers: 4 } });++++ const partyRes = await app.inject({++ method: "POST",++ url: "/v1/parties",++ payload: { leaderSteamId: steamId },++ });++ expect(partyRes.statusCode).toBe(200);++ const party = partyRes.json();++++ const inviteRes = await app.inject({++ method: "POST",++ url: "/v1/parties/invite",++ payload: { partyId: party.partyId, invitedBySteamId: steamId, invitedSteamId: friendSteamId },++ });++ expect(inviteRes.statusCode).toBe(200);++++ const acceptRes = await app.inject({++ method: "POST",++ url: "/v1/parties/invite/accept",++ payload: { inviteId: inviteRes.json().inviteId, steamId: friendSteamId },++ });++ expect(acceptRes.json().members).toHaveLength(2);++++ const travelRes = await app.inject({++ method: "POST",++ url: "/v1/parties/travel",++ payload: { partyId: party.partyId, leaderSteamId: steamId, mode: "prophunt" },++ });++ expect(travelRes.statusCode).toBe(200);++ expect(travelRes.json().serverId).toBe("party-prophunt-2");++ expect(travelRes.json().reservations).toHaveLength(2);++++ await app.close();++ });++++ it("records and lists admin audit events", async () => {++ const adminSecret = "test-admin-secret";++ const app = await createApp({++ repository: new MemoryOpenVibeRepository(),++ devAuthEnabled: true,++ adminSecret,++ });++ await app.ready();++++ const event = await app.inject({++ method: "POST",++ url: "/v1/admin/audit/events",++ headers: { "x-admin-secret": adminSecret },++ payload: {++ actorSteamId: steamId,++ action: "moderation.note",++ targetSteamId: "76561198000000004",++ reason: "Testing moderation audit trail.",++ },++ });++ expect(event.statusCode).toBe(200);++ expect(event.json().action).toBe("moderation.note");++++ const list = await app.inject({++ method: "GET",++ url: "/v1/admin/audit/events?limit=10",++ headers: { "x-admin-secret": adminSecret },++ });++ expect(list.statusCode).toBe(200);++ expect(list.json().events).toHaveLength(1);++++ await app.close();++ });});@@@ -5,13 +5,20 @@ import { OpenVibeRepository } from "./domain.js";import {authDevSchema,authSteamSchema,++ acceptPartyInviteSchema,++ auditEventSchema,++ auditQuerySchema,buyItemSchema,++ createPartySchema,equipItemSchema,++ getPartyQuerySchema,getMeQuerySchema,heartbeatSchema,leaderboardQuerySchema,listServersQuerySchema,matchEndSchema,++ partyInviteSchema,++ partyTravelSchema,registerServerSchema,travelRequestSchema,upsertShopItemSchema,@@@ -226,6 +233,35 @@ export async function createApp(options: AppOptions): Promise<FastifyInstance> {return reservation;});++ app.post("/v1/parties", async (request) => {++ const body = createPartySchema.parse(request.body);++ return options.repository.createParty(body);++ });++++ app.get("/v1/parties", async (request, reply) => {++ const query = getPartyQuerySchema.parse(request.query);++ const party = await options.repository.getParty(query.partyId);++ if (!party) return reply.code(404).send({ error: "party_not_found" });++ return party;++ });++++ app.post("/v1/parties/invite", async (request) => {++ const body = partyInviteSchema.parse(request.body);++ return options.repository.inviteToParty(body);++ });++++ app.post("/v1/parties/invite/accept", async (request) => {++ const body = acceptPartyInviteSchema.parse(request.body);++ return options.repository.acceptPartyInvite(body);++ });++++ app.post("/v1/parties/travel", async (request, reply) => {++ const body = partyTravelSchema.parse(request.body);++ const reservation = await options.repository.reservePartyTravel(body);++ if (!reservation) return reply.code(404).send({ error: "no_server_with_party_capacity" });++ return reservation;++ });++app.post("/v1/travel/validate", async (request) => {const body = validateJoinTokenSchema.parse(request.body);return options.repository.validateJoinToken(body);@@@ -257,6 +293,34 @@ export async function createApp(options: AppOptions): Promise<FastifyInstance> {return options.repository.upsertShopItem(body);});++ app.post("/v1/admin/audit/events", async (request, reply) => {++ if (!requireAdmin(request, reply)) return;++ const body = auditEventSchema.parse(request.body);++ return options.repository.recordAuditEvent(body);++ });++++ app.get("/v1/admin/audit/events", async (request, reply) => {++ if (!requireAdmin(request, reply)) return;++ const query = auditQuerySchema.parse(request.query);++ return { events: await options.repository.listAuditEvents(query) };++ });++++ app.get("/metrics", async (_request, reply) => {++ const servers = await options.repository.listServers();++ const open = servers.filter((server) => server.state === "open").length;++ const players = servers.reduce((sum, server) => sum + server.playerCount, 0);++ reply.type("text/plain; version=0.0.4");++ return [++ "# HELP openvibe_servers_open Number of open OpenVibe Source servers.",++ "# TYPE openvibe_servers_open gauge",++ `openvibe_servers_open ${open}`,++ "# HELP openvibe_players_online Current players reported by live servers.",++ "# TYPE openvibe_players_online gauge",++ `openvibe_players_online ${players}`,++ "",++ ].join("\n");++ });++app.addHook("onClose", async () => {await options.sessionStore?.close?.();});@@@ -82,6 +82,45 @@ export interface TravelReservation {expiresAt: string;}++export interface PartyMember {++ steamId: string;++ displayName: string;++ leader: boolean;++ joinedAt: string;++}++++export interface Party {++ partyId: string;++ leaderSteamId: string;++ members: PartyMember[];++}++++export interface PartyInvite {++ inviteId: string;++ partyId: string;++ invitedSteamId: string;++ invitedBySteamId: string;++ status: "pending" | "accepted" | "declined" | "expired";++ expiresAt: string;++}++++export interface PartyTravelReservation {++ partyId: string;++ mode: GameMode;++ serverId: string;++ connect: string;++ reservations: TravelReservation[];++}++++export interface AuditEvent {++ auditId: string;++ actorSteamId: string;++ action: string;++ targetSteamId: string | null;++ reason: string;++ createdAt: string;++}++export interface JoinTokenValidation {valid: boolean;serverId?: string;@@@ -120,6 +159,19 @@ export interface OpenVibeRepository {heartbeat(input: HeartbeatInput): Promise<GameServer | null>;listServers(mode?: GameMode): Promise<GameServer[]>;reserveTravel(input: { steamId: string; mode: GameMode }): Promise<TravelReservation | null>;++ createParty(input: { leaderSteamId: string }): Promise<Party>;++ inviteToParty(input: {++ partyId: string;++ invitedBySteamId: string;++ invitedSteamId: string;++ }): Promise<PartyInvite>;++ acceptPartyInvite(input: { inviteId: string; steamId: string }): Promise<Party>;++ getParty(partyId: string): Promise<Party | null>;++ reservePartyTravel(input: {++ partyId: string;++ leaderSteamId: string;++ mode: GameMode;++ }): Promise<PartyTravelReservation | null>;validateJoinToken(input: {token: string;steamId: string;@@@ -128,4 +180,11 @@ export interface OpenVibeRepository {recordMatchReward(input: MatchRewardInput): Promise<PlayerProfile | null>;getLeaderboard(options: { limit: number; mode?: GameMode }): Promise<LeaderboardEntry[]>;upsertShopItem(input: UpsertShopItemInput): Promise<ShopItem>;++ recordAuditEvent(input: {++ actorSteamId: string;++ action: string;++ targetSteamId?: string | null;++ reason: string;++ }): Promise<AuditEvent>;++ listAuditEvents(options: { limit: number }): Promise<AuditEvent[]>;}@@@ -4,10 +4,14 @@ import {GameServer,HeartbeatInput,InventoryItem,++ AuditEvent,JoinTokenValidation,LeaderboardEntry,MatchRewardInput,OpenVibeRepository,++ Party,++ PartyInvite,++ PartyTravelReservation,Player,PlayerProfile,RegisterServerInput,@@@ -29,6 +33,13 @@ interface StoredToken {consumedAt: string | null;}++interface StoredParty {++ partyId: string;++ leaderSteamId: string;++ members: Set<string>;++ joinedAt: Map<string, string>;++}++const seedShop: ShopItem[] = [{itemId: "model_rebel",@@@ -56,6 +67,9 @@ export class MemoryOpenVibeRepository implements OpenVibeRepository {private inventory = new Map<string, Set<string>>();private servers = new Map<string, StoredServer>();private tokens = new Map<string, StoredToken>();++ private parties = new Map<string, StoredParty>();++ private invites = new Map<string, PartyInvite>();++ private auditEvents: AuditEvent[] = [];private rewardKeys = new Set<string>();async upsertDevPlayer(input: { steamId: string; displayName: string }): Promise<PlayerProfile> {@@@ -194,6 +208,110 @@ export class MemoryOpenVibeRepository implements OpenVibeRepository {};}++ async createParty(input: { leaderSteamId: string }): Promise<Party> {++ this.requirePlayer(input.leaderSteamId);++ const now = new Date().toISOString();++ const party: StoredParty = {++ partyId: nanoid(16),++ leaderSteamId: input.leaderSteamId,++ members: new Set([input.leaderSteamId]),++ joinedAt: new Map([[input.leaderSteamId, now]]),++ };++ this.parties.set(party.partyId, party);++ return this.publicParty(party);++ }++++ async inviteToParty(input: {++ partyId: string;++ invitedBySteamId: string;++ invitedSteamId: string;++ }): Promise<PartyInvite> {++ const party = this.requireParty(input.partyId);++ this.requirePlayer(input.invitedBySteamId);++ this.requirePlayer(input.invitedSteamId);++ if (!party.members.has(input.invitedBySteamId)) throw new RepositoryError("not_party_member", 403);++++ const invite: PartyInvite = {++ inviteId: nanoid(16),++ partyId: input.partyId,++ invitedBySteamId: input.invitedBySteamId,++ invitedSteamId: input.invitedSteamId,++ status: "pending",++ expiresAt: new Date(Date.now() + 300_000).toISOString(),++ };++ this.invites.set(invite.inviteId, invite);++ return { ...invite };++ }++++ async acceptPartyInvite(input: { inviteId: string; steamId: string }): Promise<Party> {++ const invite = this.invites.get(input.inviteId);++ if (!invite || invite.status !== "pending") throw new RepositoryError("invite_not_found", 404);++ if (invite.invitedSteamId !== input.steamId) throw new RepositoryError("invite_forbidden", 403);++ if (new Date(invite.expiresAt).getTime() <= Date.now()) {++ invite.status = "expired";++ throw new RepositoryError("invite_expired", 409);++ }++++ const party = this.requireParty(invite.partyId);++ party.members.add(input.steamId);++ party.joinedAt.set(input.steamId, new Date().toISOString());++ invite.status = "accepted";++ return this.publicParty(party);++ }++++ async getParty(partyId: string): Promise<Party | null> {++ const party = this.parties.get(partyId);++ return party ? this.publicParty(party) : null;++ }++++ async reservePartyTravel(input: {++ partyId: string;++ leaderSteamId: string;++ mode: GameMode;++ }): Promise<PartyTravelReservation | null> {++ const party = this.requireParty(input.partyId);++ if (party.leaderSteamId !== input.leaderSteamId) {++ throw new RepositoryError("party_leader_required", 403);++ }++++ const members = [...party.members];++ const server = [...this.servers.values()]++ .filter((candidate) => candidate.mode === input.mode)++ .filter((candidate) => candidate.state === "open")++ .filter((candidate) => candidate.maxPlayers - candidate.playerCount >= members.length)++ .sort((a, b) => b.playerCount - a.playerCount)[0];++++ if (!server) return null;++++ const reservations = members.map((steamId) => {++ const expiresAt = new Date(Date.now() + 90_000).toISOString();++ const token = nanoid(32);++ this.tokens.set(token, {++ token,++ steamId,++ serverId: server.serverId,++ mode: input.mode,++ expiresAt,++ consumedAt: null,++ });++ return {++ mode: input.mode,++ serverId: server.serverId,++ connect: `${server.publicHost}:${server.port}`,++ joinToken: token,++ expiresAt,++ };++ });++++ return {++ partyId: input.partyId,++ mode: input.mode,++ serverId: server.serverId,++ connect: `${server.publicHost}:${server.port}`,++ reservations,++ };++ }++async validateJoinToken(input: {token: string;steamId: string;@@@ -273,11 +391,33 @@ export class MemoryOpenVibeRepository implements OpenVibeRepository {return item;}++ private requireParty(partyId: string): StoredParty {++ const party = this.parties.get(partyId);++ if (!party) throw new RepositoryError("party_not_found", 404);++ return party;++ }++private publicServer(server: StoredServer): GameServer {const { serverSecret: _serverSecret, ...safe } = server;return { ...safe };}++ private publicParty(party: StoredParty): Party {++ return {++ partyId: party.partyId,++ leaderSteamId: party.leaderSteamId,++ members: [...party.members].map((steamId) => {++ const player = this.requirePlayer(steamId);++ return {++ steamId,++ displayName: player.displayName,++ leader: steamId === party.leaderSteamId,++ joinedAt: party.joinedAt.get(steamId) ?? new Date(0).toISOString(),++ };++ }),++ };++ }++async getLeaderboard(options: { limit: number; mode?: GameMode }): Promise<LeaderboardEntry[]> {const players = [...this.players.values()];const sorted = players@@@ -298,6 +438,28 @@ export class MemoryOpenVibeRepository implements OpenVibeRepository {this.shop.set(item.itemId, item);return { ...item };}++++ async recordAuditEvent(input: {++ actorSteamId: string;++ action: string;++ targetSteamId?: string | null;++ reason: string;++ }): Promise<AuditEvent> {++ const event: AuditEvent = {++ auditId: nanoid(16),++ actorSteamId: input.actorSteamId,++ action: input.action,++ targetSteamId: input.targetSteamId ?? null,++ reason: input.reason,++ createdAt: new Date().toISOString(),++ };++ this.auditEvents.unshift(event);++ return { ...event };++ }++++ async listAuditEvents(options: { limit: number }): Promise<AuditEvent[]> {++ return this.auditEvents.slice(0, options.limit).map((event) => ({ ...event }));++ }}export class RepositoryError extends Error {@@@ -4,10 +4,14 @@ import {GameServer,HeartbeatInput,InventoryItem,++ AuditEvent,JoinTokenValidation,LeaderboardEntry,MatchRewardInput,OpenVibeRepository,++ Party,++ PartyInvite,++ PartyTravelReservation,Player,PlayerProfile,RegisterServerInput,@@@ -316,6 +320,165 @@ export class PgOpenVibeRepository implements OpenVibeRepository {};}++ async createParty(input: { leaderSteamId: string }): Promise<Party> {++ const partyId = nanoid(16);++ const client = await this.pool.connect();++ try {++ await client.query("BEGIN");++ await client.query(++ "INSERT INTO parties (party_id, leader_steam_id) VALUES ($1, $2)",++ [partyId, input.leaderSteamId],++ );++ await client.query(++ "INSERT INTO party_members (party_id, steam_id) VALUES ($1, $2)",++ [partyId, input.leaderSteamId],++ );++ await client.query("COMMIT");++ } catch (error) {++ await client.query("ROLLBACK");++ throw error;++ } finally {++ client.release();++ }++ const party = await this.getParty(partyId);++ if (!party) throw new RepositoryError("party_not_found", 404);++ return party;++ }++++ async inviteToParty(input: {++ partyId: string;++ invitedBySteamId: string;++ invitedSteamId: string;++ }): Promise<PartyInvite> {++ const member = await this.pool.query(++ "SELECT 1 FROM party_members WHERE party_id = $1 AND steam_id = $2",++ [input.partyId, input.invitedBySteamId],++ );++ if (member.rowCount !== 1) throw new RepositoryError("not_party_member", 403);++++ const inviteId = nanoid(16);++ const result = await this.pool.query(++ `++ INSERT INTO party_invites (++ invite_id, party_id, invited_by_steam_id, invited_steam_id, status, expires_at++ )++ VALUES ($1, $2, $3, $4, 'pending', now() + interval '5 minutes')++ RETURNING invite_id, party_id, invited_by_steam_id, invited_steam_id, status, expires_at++ `,++ [inviteId, input.partyId, input.invitedBySteamId, input.invitedSteamId],++ );++ return mapPartyInvite(result.rows[0]);++ }++++ async acceptPartyInvite(input: { inviteId: string; steamId: string }): Promise<Party> {++ const client = await this.pool.connect();++ let partyId = "";++ try {++ await client.query("BEGIN");++ const invite = await client.query(++ `++ UPDATE party_invites++ SET status = 'accepted'++ WHERE invite_id = $1++ AND invited_steam_id = $2++ AND status = 'pending'++ AND expires_at > now()++ RETURNING party_id++ `,++ [input.inviteId, input.steamId],++ );++ if (invite.rowCount !== 1) throw new RepositoryError("invite_not_found", 404);++ partyId = String(invite.rows[0].party_id);++ await client.query(++ `++ INSERT INTO party_members (party_id, steam_id)++ VALUES ($1, $2)++ ON CONFLICT DO NOTHING++ `,++ [partyId, input.steamId],++ );++ await client.query("COMMIT");++ } catch (error) {++ await client.query("ROLLBACK");++ throw error;++ } finally {++ client.release();++ }++ const party = await this.getParty(partyId);++ if (!party) throw new RepositoryError("party_not_found", 404);++ return party;++ }++++ async getParty(partyId: string): Promise<Party | null> {++ const result = await this.pool.query(++ `++ SELECT p.party_id, p.leader_steam_id, pm.steam_id, pm.joined_at, pl.display_name++ FROM parties p++ JOIN party_members pm ON pm.party_id = p.party_id++ JOIN players pl ON pl.steam_id = pm.steam_id++ WHERE p.party_id = $1++ ORDER BY pm.joined_at++ `,++ [partyId],++ );++ if (result.rowCount === 0) return null;++ return mapParty(result.rows);++ }++++ async reservePartyTravel(input: {++ partyId: string;++ leaderSteamId: string;++ mode: GameMode;++ }): Promise<PartyTravelReservation | null> {++ const party = await this.getParty(input.partyId);++ if (!party) throw new RepositoryError("party_not_found", 404);++ if (party.leaderSteamId !== input.leaderSteamId) throw new RepositoryError("party_leader_required", 403);++++ const serverResult = await this.pool.query(++ `++ SELECT server_id, public_host, port++ FROM game_servers++ WHERE mode = $1++ AND state = 'open'++ AND max_players - player_count >= $2++ AND last_heartbeat > now() - interval '60 seconds'++ ORDER BY player_count DESC, last_heartbeat DESC++ LIMIT 1++ `,++ [input.mode, party.members.length],++ );++ if (serverResult.rowCount !== 1) return null;++++ const server = serverResult.rows[0];++ const reservations: TravelReservation[] = [];++ for (const member of party.members) {++ const token = nanoid(32);++ const tokenResult = await this.pool.query(++ `++ INSERT INTO join_tokens (token, steam_id, server_id, mode, expires_at)++ VALUES ($1, $2, $3, $4, now() + interval '90 seconds')++ RETURNING expires_at++ `,++ [token, member.steamId, server.server_id, input.mode],++ );++ reservations.push({++ mode: input.mode,++ serverId: String(server.server_id),++ connect: `${server.public_host}:${server.port}`,++ joinToken: token,++ expiresAt: new Date(tokenResult.rows[0].expires_at).toISOString(),++ });++ }++++ return {++ partyId: input.partyId,++ mode: input.mode,++ serverId: String(server.server_id),++ connect: `${server.public_host}:${server.port}`,++ reservations,++ };++ }++async validateJoinToken(input: {token: string;steamId: string;@@@ -456,6 +619,36 @@ export class PgOpenVibeRepository implements OpenVibeRepository {);return mapShopItem(result.rows[0]);}++++ async recordAuditEvent(input: {++ actorSteamId: string;++ action: string;++ targetSteamId?: string | null;++ reason: string;++ }): Promise<AuditEvent> {++ const result = await this.pool.query(++ `++ INSERT INTO audit_events (audit_id, actor_steam_id, action, target_steam_id, reason)++ VALUES ($1, $2, $3, $4, $5)++ RETURNING audit_id, actor_steam_id, action, target_steam_id, reason, created_at++ `,++ [nanoid(16), input.actorSteamId, input.action, input.targetSteamId ?? null, input.reason],++ );++ return mapAuditEvent(result.rows[0]);++ }++++ async listAuditEvents(options: { limit: number }): Promise<AuditEvent[]> {++ const result = await this.pool.query(++ `++ SELECT audit_id, actor_steam_id, action, target_steam_id, reason, created_at++ FROM audit_events++ ORDER BY created_at DESC++ LIMIT $1++ `,++ [options.limit],++ );++ return result.rows.map(mapAuditEvent);++ }}function mapPlayer(row: Record<string, unknown>): Player {@@@ -501,3 +694,43 @@ function mapServer(row: Record<string, unknown>): GameServer {lastHeartbeat: new Date(row.last_heartbeat as string | Date).toISOString(),};}++++function mapPartyInvite(row: Record<string, unknown>): PartyInvite {++ return {++ inviteId: String(row.invite_id),++ partyId: String(row.party_id),++ invitedBySteamId: String(row.invited_by_steam_id),++ invitedSteamId: String(row.invited_steam_id),++ status: row.status as PartyInvite["status"],++ expiresAt: new Date(row.expires_at as string | Date).toISOString(),++ };++}++++function mapParty(rows: Record<string, unknown>[]): Party {++ const first = rows[0];++ const leaderSteamId = String(first.leader_steam_id);++ return {++ partyId: String(first.party_id),++ leaderSteamId,++ members: rows.map((row) => {++ const steamId = String(row.steam_id);++ return {++ steamId,++ displayName: String(row.display_name),++ leader: steamId === leaderSteamId,++ joinedAt: new Date(row.joined_at as string | Date).toISOString(),++ };++ }),++ };++}++++function mapAuditEvent(row: Record<string, unknown>): AuditEvent {++ return {++ auditId: String(row.audit_id),++ actorSteamId: String(row.actor_steam_id),++ action: String(row.action),++ targetSteamId: row.target_steam_id ? String(row.target_steam_id) : null,++ reason: String(row.reason),++ createdAt: new Date(row.created_at as string | Date).toISOString(),++ };++}@@@ -56,6 +56,31 @@ export const travelRequestSchema = z.object({mode,});++export const createPartySchema = z.object({++ leaderSteamId: steamId,++});++++export const partyInviteSchema = z.object({++ partyId: z.string().min(8).max(64),++ invitedBySteamId: steamId,++ invitedSteamId: steamId,++});++++export const acceptPartyInviteSchema = z.object({++ inviteId: z.string().min(8).max(64),++ steamId,++});++++export const getPartyQuerySchema = z.object({++ partyId: z.string().min(8).max(64),++});++++export const partyTravelSchema = z.object({++ partyId: z.string().min(8).max(64),++ leaderSteamId: steamId,++ mode,++});++export const validateJoinTokenSchema = z.object({token: z.string().min(16).max(128),steamId,@@@ -87,3 +112,14 @@ export const upsertShopItemSchema = z.object({price: z.number().int().min(0).max(100_000),enabled: z.boolean().default(true),});++++export const auditEventSchema = z.object({++ actorSteamId: steamId,++ action: z.string().trim().min(1).max(80),++ targetSteamId: steamId.optional(),++ reason: z.string().trim().min(1).max(500),++});++++export const auditQuerySchema = z.object({++ limit: z.coerce.number().int().min(1).max(200).default(50),++});@@@ -69,20 +69,22 @@ npm run buildnpm test```--Current result: TypeScript build passes and 9 tests pass.++Current result: TypeScript build passes and 11 tests pass.## Maps And SRCDS--The VMF generator now emits portal commands using `ov_join`:++The VMF generator currently emits direct local connects for the Proton fallback:```text--ov_join hub--ov_join prophunt--ov_join deathrun--ov_join fortwars--ov_join traitortown++connect 127.0.0.1:27015++connect 127.0.0.2:27016++connect 127.0.0.3:27017++connect 127.0.0.4:27018++connect 127.0.0.5:27019```++Set `OPENVIBE_USE_OV_JOIN=1` while regenerating VMFs to bake authenticated `ov_join <mode>` commands.++Compiled and smoke-tested BSPs:```text@@@ -87,7 +87,7 @@ VScript cannot make HTTP requests directly. The sidecar bridges the gap:## C++ Travel Flow--Portal pads now call `ov_join <mode>` instead of hardcoded `connect` commands:++The authenticated travel flow is implemented for rebuilt OpenVibe client DLLs:```text1. Player steps on portal pad.@@@ -99,6 +99,8 @@ Portal pads now call `ov_join <mode>` instead of hardcoded `connect` commands:7. Server C++ validates the token with `/v1/travel/validate`.```++The checked-in dev BSPs currently use direct local `connect` commands as a Proton fallback. Regenerate VMFs with `OPENVIBE_USE_OV_JOIN=1` to bake the authenticated path into portal pads.++## GameDLL Feature HooksTracked C++ patch sources live under `sdk/openvibe/` and are copied into the local Valve SDK checkout by `tools/apply-openvibe-sdk.sh`.nnew file mode 100644@@@ -0,0 +1,8 @@++// OpenVibe: Source client fallback aliases.++//++// The Linux64 SDK build provides real C++ implementations for these commands.++// Proton/Windows clients may still load older DLLs until a Windows GameDLL is++// built, so these aliases prevent noisy "Unknown command" console output.++alias "openvibe_menu" "echo OpenVibe: Source menu is available through GameMenu.res or the launcher."++alias "ov_menu" "openvibe_menu"++alias "ov_join" "echo OpenVibe: authenticated ov_join requires the custom OpenVibe client DLL."nnew file mode 100644@@@ -0,0 +1 @@++Welcome to OpenVibe: Source.nnew file mode 100644@@@ -0,0 +1 @@++Welcome to OpenVibe: Source.nnew file mode 100644@@@ -0,0 +1,32 @@++whitelist++{++ // Server rules used for sv_pure.++ //++ // Do not modify this file.++ //++ // See pure_server_whitelist_example.txt for more details on when this file is used and what the rules mean.++++ //++ // Enforce consistency for a few critical files++ //++++ particles/bullet_tracers.pcf trusted_source++ particles/burningplayer.pcf trusted_source++ particles/burningplayer_dx80.pcf trusted_source++ particles/cig_smoke.pcf trusted_source++ particles/cig_smoke_dx80.pcf trusted_source++ particles/explosion.pcf trusted_source++ particles/explosion_dx80.pcf trusted_source++ particles/explosion_dx90_slow.pcf trusted_source++ particles/explosion_high.pcf trusted_source++ particles/disguise.pcf trusted_source++ particles/flag_particles.pcf trusted_source++ particles/nemesis.pcf trusted_source++ particles/player_recent_teleport.pcf trusted_source++ particles/player_recent_teleport_dx80.pcf trusted_source++ particles/speechbubbles.pcf trusted_source++ particles/teleported_fx.pcf trusted_source++ particles/teleport_status.pcf trusted_source++ particles/water.pcf trusted_source++ particles/water_dx80.pcf trusted_source++}nnew file mode 100644@@@ -0,0 +1,2 @@++// OpenVibe: Source default dedicated server config.++// Individual shards load openvibe_<mode>.cfg after this file.nnew file mode 100644@@@ -0,0 +1,20 @@++"trusted_key_list"++{++++ //++ // This file lists public keys that are trusted by the server.++ // It should usually only contain official Valve public keys,++ // so don't modify it.++ //++ // See trusted_key_example.txt for how you can add keys issues++ // by third parties, in order to add their content to the list++ // of trusted content.++ //++++ // Official Valve content. Key generated 2013/03/01++ public_key++ {++ type "rsa"++ rsa_public_key "30819D300D06092A864886F70D010101050003818B0030818702818100B1C0F11CB2982F29259507A774D4834377C5B7A38D9A4B3892B598009F16AA109565CB09AD25DE0D3D1A089C3CB68E491921CC142F383383201DE98262A76ED8A6CC78BC51685A0A64A6172C67127AF23E78731F4A82C201D64C9AB80937322184B642727FE142D15CC045F3583E19E3E3E1A9C50C0FC84113573A520A8F7323020111"++ }++}nnew file mode 100644@@@ -0,0 +1,2 @@++// OpenVibe: Source startup script.++// Intentionally small; mode-specific server config is passed with +exec.BBinary files a/game/openvibe.games/maps/dr_openvibe_dev.bsp and b/game/openvibe.games/maps/dr_openvibe_dev.bsp differBBinary files a/game/openvibe.games/maps/fw_openvibe_dev.bsp and b/game/openvibe.games/maps/fw_openvibe_dev.bsp differBBinary files a/game/openvibe.games/maps/ov_hub.bsp and b/game/openvibe.games/maps/ov_hub.bsp differBBinary files a/game/openvibe.games/maps/ph_openvibe_dev.bsp and b/game/openvibe.games/maps/ph_openvibe_dev.bsp differBBinary files a/game/openvibe.games/maps/tt_openvibe_dev.bsp and b/game/openvibe.games/maps/tt_openvibe_dev.bsp differnnew file mode 100644@@@ -0,0 +1,7 @@++"UnlitGeneric"++{++ "$basetexture" "vgui/white_additive"++ "$translucent" "1"++ "$vertexcolor" "1"++ "$vertexalpha" "1"++}BBinary files a/game/openvibe.games/modelsounds.cache and b/game/openvibe.games/modelsounds.cache differnnew file mode 100644@@@ -0,0 +1,1285 @@++///////////////////////////////////////////////////////////++// Tracker scheme resource file++//++// sections:++// Colors - all the colors used by the scheme++// BaseSettings - contains settings for app to use to draw controls++// Fonts - list of all the fonts used by app++// Borders - description of all the borders++//++///////////////////////////////////////////////////////////++Scheme++{++ //////////////////////// COLORS ///////////////////////////++ // color details++ // this is a list of all the colors used by the scheme++ Colors++ {++ // base colors++ "Orange" "255 176 0 255"++ "OrangeDim" "255 176 0 120"++ "LightOrange" "188 112 0 128"++++ "Red" "192 28 0 140"++ "Black" "0 0 0 196"++ "TransparentBlack" "0 0 0 196"++ "TransparentLightBlack" "0 0 0 90"++++ "Blank" "0 0 0 0"++ "ForTesting" "255 0 0 32"++ "ForTesting_Magenta" "255 0 255 255"++ "ForTesting_MagentaDim" "255 0 255 120"++ }++++ ///////////////////// BASE SETTINGS ////////////////////////++ //++ // default settings for all panels++ // controls use these to determine their settings++ BaseSettings++ {++ "FgColor" "255 220 0 100"++ "FgColor_vrmode" "255 220 0 200"++ "BgColor" "0 0 0 76"++++ "Panel.FgColor" "255 220 0 100"++ "Panel.BgColor" "0 0 0 76"++++ "BrightFg" "255 220 0 255"++++ "DamagedBg" "180 0 0 200"++ "DamagedFg" "180 0 0 230"++ "BrightDamagedFg" "255 0 0 255"++++ // weapon selection colors++ "SelectionNumberFg" "255 220 0 255"++ "SelectionTextFg" "255 220 0 255"++ "SelectionEmptyBoxBg" "0 0 0 80"++ "SelectionBoxBg" "0 0 0 80"++ "SelectionSelectedBoxBg" "0 0 0 80"++++ "ZoomReticleColor" "255 220 0 255"++++ // HL1-style HUD colors++ "Yellowish" "255 160 0 255"++ "Normal" "255 208 64 255"++ "Caution" "255 48 0 255"++++ // Top-left corner of the "Half-Life 2" on the main screen++ "Main.Title1.X" "76"++ "Main.Title1.Y" "184"++ "Main.Title1.Color" "255 255 255 255"++++ // Top-left corner of the "DEATHMATCH" on the main screen++ "Main.Title2.X" "315"++ "Main.Title2.Y" "222"++ "Main.Title2.Color" "255 255 255 180"++++ // Top-left corner of the menu on the main screen++ "Main.Menu.X" "76"++ "Main.Menu.Y" "240"++++ // Blank space to leave beneath the menu on the main screen++ "Main.BottomBorder" "32"++++ // Deck colors++ "SteamDeckLoadingBar" "250 128 20 255"++ "SteamDeckSpinner" "201 100 0 255"++ "SteamDeckLoadingText" "181 179 175 255"++++++///HERE++ // vgui_controls color specifications++ Border.Bright "LightOrange" // the lit side of a control++ Border.Dark "LightOrange" // the dark/unlit side of a control++ Border.Selection "Blank" // the additional border color for displaying the default/selected button++++ Button.TextColor "Orange"++ Button.BgColor "Blank"++ Button.ArmedTextColor "Orange"++ Button.ArmedBgColor "Red"++ Button.DepressedTextColor "Orange"++ Button.DepressedBgColor "Red"++++ CheckButton.TextColor "Orange"++ CheckButton.SelectedTextColor "Orange"++ CheckButton.BgColor "TransparentBlack"++ CheckButton.Border1 "Border.Dark" // the left checkbutton border++ CheckButton.Border2 "Border.Bright" // the right checkbutton border++ CheckButton.Check "Orange" // color of the check itself++++ ComboBoxButton.ArrowColor "Orange"++ ComboBoxButton.ArmedArrowColor "Orange"++ ComboBoxButton.BgColor "TransparentBlack"++ ComboBoxButton.DisabledBgColor "Blank"++++ Frame.BgColor "TransparentBlack"++ Frame.OutOfFocusBgColor "TransparentBlack"++ Frame.FocusTransitionEffectTime "0.0" // time it takes for a window to fade in/out on focus/out of focus++ Frame.TransitionEffectTime "0.0" // time it takes for a window to fade in/out on open/close++ Frame.AutoSnapRange "0"++ FrameGrip.Color1 "Blank"++ FrameGrip.Color2 "Blank"++ FrameTitleButton.FgColor "Blank"++ FrameTitleButton.BgColor "Blank"++ FrameTitleButton.DisabledFgColor "Blank"++ FrameTitleButton.DisabledBgColor "Blank"++ FrameSystemButton.FgColor "Blank"++ FrameSystemButton.BgColor "Blank"++ FrameSystemButton.Icon ""++ FrameSystemButton.DisabledIcon ""++ FrameTitleBar.TextColor "Orange"++ FrameTitleBar.BgColor "Blank"++ FrameTitleBar.DisabledTextColor "Orange"++ FrameTitleBar.DisabledBgColor "Blank"++++ GraphPanel.FgColor "Orange"++ GraphPanel.BgColor "TransparentBlack"++++ Label.TextDullColor "Orange"++ Label.TextColor "Orange"++ Label.TextBrightColor "Orange"++ Label.SelectedTextColor "Orange"++ Label.BgColor "Blank"++ Label.DisabledFgColor1 "Blank"++ Label.DisabledFgColor2 "LightOrange"++++ ListPanel.TextColor "Orange"++ ListPanel.BgColor "TransparentBlack"++ ListPanel.SelectedTextColor "Black"++ ListPanel.SelectedBgColor "Red"++ ListPanel.SelectedOutOfFocusBgColor "Red"++ ListPanel.EmptyListInfoTextColor "Orange"++++ Menu.TextColor "Orange"++ Menu.BgColor "TransparentBlack"++ Menu.ArmedTextColor "Orange"++ Menu.ArmedBgColor "Red"++ Menu.TextInset "6"++++ Chat.TypingText "Orange"++++ Panel.FgColor "OrangeDim"++ Panel.BgColor "blank"++++ ProgressBar.FgColor "Orange"++ ProgressBar.BgColor "TransparentBlack"++++ PropertySheet.TextColor "Orange"++ PropertySheet.SelectedTextColor "Orange"++ PropertySheet.TransitionEffectTime "0.25" // time to change from one tab to another++++ RadioButton.TextColor "Orange"++ RadioButton.SelectedTextColor "Orange"++++ RichText.TextColor "Orange"++ RichText.BgColor "Blank"++ RichText.SelectedTextColor "Orange"++ RichText.SelectedBgColor "Blank"++++ ScrollBarButton.FgColor "Orange"++ ScrollBarButton.BgColor "Blank"++ ScrollBarButton.ArmedFgColor "Orange"++ ScrollBarButton.ArmedBgColor "Blank"++ ScrollBarButton.DepressedFgColor "Orange"++ ScrollBarButton.DepressedBgColor "Blank"++++ ScrollBarSlider.FgColor "Blank" // nob color++ ScrollBarSlider.BgColor "Blank" // slider background color++++ SectionedListPanel.HeaderTextColor "Orange"++ SectionedListPanel.HeaderBgColor "Blank"++ SectionedListPanel.DividerColor "Black"++ SectionedListPanel.TextColor "Orange"++ SectionedListPanel.BrightTextColor "Orange"++ SectionedListPanel.BgColor "TransparentLightBlack"++ SectionedListPanel.SelectedTextColor "Black"++ SectionedListPanel.SelectedBgColor "Red"++ SectionedListPanel.OutOfFocusSelectedTextColor "Black"++ SectionedListPanel.OutOfFocusSelectedBgColor "255 255 255 32"++++ Slider.NobColor "108 108 108 255"++ Slider.TextColor "127 140 127 255"++ Slider.TrackColor "31 31 31 255"++ Slider.DisabledTextColor1 "117 117 117 255"++ Slider.DisabledTextColor2 "30 30 30 255"++++ TextEntry.TextColor "Orange"++ TextEntry.BgColor "TransparentBlack"++ TextEntry.CursorColor "Orange"++ TextEntry.DisabledTextColor "Orange"++ TextEntry.DisabledBgColor "Blank"++ TextEntry.SelectedTextColor "Black"++ TextEntry.SelectedBgColor "Red"++ TextEntry.OutOfFocusSelectedBgColor "Red"++ TextEntry.FocusEdgeColor "TransparentBlack"++++ ToggleButton.SelectedTextColor "Orange"++++ Tooltip.TextColor "TransparentBlack"++ Tooltip.BgColor "Red"++++ TreeView.BgColor "TransparentBlack"++++ WizardSubPanel.BgColor "Blank"++++ // scheme-specific colors++ "FgColor" "Orange"++ "BgColor" "TransparentBlack"++++ "ViewportBG" "Blank"++ "team0" "204 204 204 255" // Spectators++ "team1" "255 64 64 255" // CT's++ "team2" "153 204 255 255" // T's++++ "MapDescriptionText" "Orange" // the text used in the map description window++ "CT_Blue" "153 204 255 255"++ "T_Red" "255 64 64 255"++ "Hostage_Yellow" "Panel.FgColor"++ "HudIcon_Green" "0 160 0 255"++ "HudIcon_Red" "160 0 0 255"++++ // CHudMenu++ "ItemColor" "255 167 42 200" // default 255 167 42 255++ "MenuColor" "233 208 173 255"++ "MenuBoxBg" "0 0 0 100"++++ // weapon selection colors++ "SelectionNumberFg" "255 220 0 200"++ "SelectionTextFg" "255 220 0 200"++ "SelectionEmptyBoxBg" "0 0 0 80"++ "SelectionBoxBg" "0 0 0 80"++ "SelectionSelectedBoxBg" "0 0 0 190"++++ // Hint message colors++ "HintMessageFg" "255 255 255 255"++ "HintMessageBg" "0 0 0 60"++++ "ProgressBarFg" "255 30 13 255"++ }++++ //////////////////////// BITMAP FONT FILES /////////////////////////////++ //++ // Bitmap Fonts are ****VERY*** expensive static memory resources so they are purposely sparse++ BitmapFontFiles++ {++ // UI buttons, custom font, (256x64)++ "Buttons" "materials/vgui/fonts/buttons_32.vbf"++ }++++++ //////////////////////// FONTS /////////////////////////////++ //++ // describes all the fonts++ Fonts++ {++ // fonts are used in order that they are listed++ // fonts are used in order that they are listed++ "DebugFixed"++ {++ "1"++ {++ "name" "Courier New"++ "tall" "14"++ "weight" "400"++ "antialias" "1"++ }++ }++ // fonts are used in order that they are listed++ "DebugFixedSmall"++ {++ "1"++ {++ "name" "Courier New"++ "tall" "14"++ "weight" "400"++ "antialias" "1"++ }++ }++ // fonts listed later in the order will only be used if they fulfill a range not already filled++ // if a font fails to load then the subsequent fonts will replace++ Default++ {++ "1"++ {++ "name" "GorDIN"++ "tall" "16"++ "tall" "12"++ "weight" "600"++ "antialias" "1"++ "yres" "1 599"++ }++ "2"++ {++ "name" "GorDIN"++ "tall" "22"++ "tall" "14"++ "weight" "600"++ "antialias" "1"++ "yres" "600 767"++ }++ "3"++ {++ "name" "GorDIN"++ "tall" "26"++ "tall" "19"++ "weight" "700"++ "antialias" "1"++ "yres" "768 1023"++ }++ "4"++ {++ "name" "GorDIN"++ "tall" "30"++ "tall" "21"++ "weight" "700"++ "antialias" "1"++ "yres" "1024 1199"++ }++ "5" // Proportional - misyl++ {++ "name" "GorDIN"++ "tall" "14"++ "tall" "10"++ "weight" "700"++ "antialias" "1"++ "additive" "1"++ }++ }++ "DefaultSmall"++ {++ "1"++ {++ "name" "GorDIN"++ "tall" "12"++ "weight" "400"++ "yres" "480 599"++ "antialias" "1"++ }++ "2"++ {++ "name" "GorDIN"++ "tall" "14"++ "weight" "400"++ "yres" "600 767"++ "antialias" "1"++ }++ "3"++ {++ "name" "GorDIN"++ "tall" "18"++ "weight" "400"++ "yres" "768 1023"++ "antialias" "1"++ }++ "4"++ {++ "name" "GorDIN"++ "tall" "20"++ "weight" "400"++ "yres" "1024 1199"++ "antialias" "1"++ }++ "5" // Proportional - misyl++ {++ "name" "GorDIN"++ "tall" "12"++ "weight" "0"++ "antialias" "1"++ }++ }++ "DefaultVerySmall"++ {++ "1"++ {++ "name" "GorDIN"++ "tall" "14"++ "weight" "0"++ "yres" "480 599"++ "antialias" "1"++ }++ "2"++ {++ "name" "GorDIN"++ "tall" "15"++ "weight" "0"++ "yres" "600 767"++ "antialias" "1"++ }++ "3"++ {++ "name" "GorDIN"++ "tall" "16"++ "weight" "0"++ "yres" "768 1023"++ "antialias" "1"++ }++ "4"++ {++ "name" "GorDIN"++ "tall" "20"++ "weight" "0"++ "yres" "1024 1199"++ "antialias" "1"++ }++ "5" // Proportional - misyl++ {++ "name" "GorDIN"++ "tall" "12"++ "weight" "0"++ "antialias" "1"++ }++ }++ WeaponIcons++ {++ "1"++ {++ "name" "HalfLife2"++ "tall" "70"++ "tall" "64"++ "tall_hidef" "58"++ "weight" "0"++ "antialias" "1"++ "additive" "1"++ "custom" "1"++ }++ }++ WeaponIconsSelected++ {++ "1"++ {++ "name" "HalfLife2"++ "tall" "70"++ "tall" "64"++ "tall_hidef" "58"++ "weight" "0"++ "antialias" "1"++ "blur" "5"++ "scanlines" "2"++ "additive" "1"++ "custom" "1"++ }++ }++ WeaponIconsSmall++ {++ "1"++ {++ "name" "HalfLife2"++ "tall" "36"++ "tall" "32"++ "weight" "0"++ "antialias" "1"++ "additive" "1"++ "custom" "1"++ }++ }++ WeaponIconsSmall_ABitSmaller++ {++ "1"++ {++ "name" "HalfLife2"++ "tall" "34"++ "tall" "27"++ "weight" "0"++ "antialias" "1"++ "additive" "1"++ "custom" "1"++ }++ }++ WeaponIconsSmallSecondary++ {++ "1"++ {++ "name" "HalfLife2"++ "tall" "34"++ "tall" "25"++ "weight" "0"++ "antialias" "1"++ "additive" "1"++ "custom" "1"++ }++ }++ FlashlightDeck++ {++ "1"++ {++ "name" "HalfLife2"++ "tall" "46"++ "weight" "0"++ "antialias" "1"++ "additive" "1"++ "custom" "1"++ }++ }++ Crosshairs++ {++ "1"++ {++ "name" "HalfLife2"++ "tall" "40"++ "tall" "41"++ "weight" "0"++ "antialias" "0"++ "additive" "1"++ "custom" "1"++ "yres" "1 1599"++ "yres" "1 1439"++ }++ "2"++ {++ "name" "HalfLife2"++ "tall" "80"++ "weight" "0"++ "antialias" "1"++ "additive" "1"++ "custom" "1"++ "yres" "1600 3199"++ "yres" "1440 3199"++ }++ "3"++ {++ "name" "HalfLife2"++ "tall" "120"++ "weight" "0"++ "antialias" "1"++ "additive" "1"++ "custom" "1"++ "yres" "3200 4799"++ }++ "4"++ {++ "name" "HalfLife2"++ "tall" "17"++ "weight" "0"++ "antialias" "1"++ "additive" "1"++ "custom" "1"++ }++ }++ QuickInfo++ {++ "1"++ {++ "name" "HL2cross"++ "tall" "36"++ "tall" "28"++ "tall" "50"++ "weight" "0"++ "antialias" "1"++ "additive" "1"++ "custom" "1"++ }++ }++ HudNumbers++ {++ "1"++ {++ "name" "HalfLife2"++ "tall" "32"++ "tall" "40"++ "weight" "0"++ "antialias" "1"++ "additive" "1"++ "custom" "1"++ }++ }++ HudNumbersGlow++ {++ "1"++ {++ "name" "HalfLife2"++ "tall" "32"++ "tall" "40"++ "weight" "0"++ "blur" "4"++ "scanlines" "2"++ "antialias" "1"++ "additive" "1"++ "custom" "1"++ }++ }++ HudNumbersSmall++ {++ "1"++ {++ "name" "HalfLife2"++ "name" "Helvetica Bold"++ "tall" "16"++ "tall" "26"++ "weight" "1000"++ "additive" "1"++ "antialias" "1"++ "custom" "1"++ }++ }++ HudSelectionNumbers++ {++ "1"++ {++ "name" "GorDIN"++ "tall" "16"++ "tall" "11"++ "weight" "700"++ "antialias" "1"++ "additive" "1"++ }++ }++ HudHintTextLarge++ {++ "1"++ {++ "name" "GorDIN"++ "tall" "22"++ "tall" "15"++ "weight" "700"++ "antialias" "1"++ "additive" "1"++ }++ }++ HudHintTextSmall++ {++ "1"++ {++ "name" "GorDIN"++ "tall" "18"++ "tall" "13"++ "weight" "0"++ "antialias" "1"++ "additive" "1"++ }++ }++ HudSelectionText++ {++ "1"++ {++ "name" "GorDIN"++ "tall" "12"++ "tall" "12"++ "weight" "600"++ "antialias" "1"++ "yres" "1 599"++ "additive" "1"++ }++ "2"++ {++ "name" "GorDIN"++ "tall" "14"++ "tall" "12"++ "weight" "600"++ "antialias" "1"++ "yres" "600 767"++ "additive" "1"++ }++ "3"++ {++ "name" "GorDIN"++ "tall" "18"++ "tall" "14"++ "weight" "600"++ "antialias" "1"++ "yres" "768 1023"++ "additive" "1"++ }++ "4"++ {++ "name" "GorDIN"++ "tall" "22"++ "tall" "17"++ "weight" "600"++ "antialias" "1"++ "yres" "1024 1199"++ "additive" "1"++ }++ "5"++ {++ "name" "GorDIN"++ "tall" "9"++ "tall" "7"++ "weight" "700"++ "antialias" "1"++ "additive" "1"++ }++ }++ GameUIButtons++ {++ "1"++ {++ "bitmap" "1"++ "name" "Buttons"++ "scalex" "0.63"++ "scaley" "0.63"++ "scalex_hidef" "1.0"++ "scaley_hidef" "1.0"++ }++ }++ BudgetLabel++ {++ "1"++ {++ "name" "Courier New"++ "tall" "14"++ "weight" "400"++ "outline" "1"++ }++ }++ DebugOverlay++ {++ "1"++ {++ "name" "Courier New"++ "tall" "14"++ "weight" "400"++ "outline" "1"++ }++ "1"++ {++ "name" "Tahoma"++ "tall" "18"++ "weight" "200"++ "outline" "1"++ }++ }++ "CloseCaption_Normal"++ {++ "1"++ {++ "name" "GorDIN"++ "tall" "15"++ "tall" "12"++ "weight" "600" // todo(misyl): do we want this at 500 or 600?++ "antialias" "1"++ }++ }++ "CloseCaption_Italic"++ {++ "1"++ {++ "name" "GorDIN"++ "tall" "15"++ "tall" "12"++ "weight" "600"++ "italic" "1"++ "antialias" "1"++ }++ }++ "CloseCaption_Bold"++ {++ "1"++ {++ "name" "GorDIN"++ "tall" "15"++ "tall" "12"++ "weight" "700"++ "antialias" "1"++ }++ }++ "CloseCaption_BoldItalic"++ {++ "1"++ {++ "name" "GorDIN"++ "tall" "15"++ "tall" "12"++ "weight" "700"++ "italic" "1"++ "antialias" "1"++ }++ }++ "CloseCaption_Small"++ {++ "1"++ {++ "name" "GorDIN"++ "tall" "15"++ "tall" "12"++ "weight" "600"++ "antialias" "1"++ }++ }++ // this is the symbol font++ "Marlett"++ {++ "1"++ {++ "name" "Marlett"++ "tall" "14"++ "weight" "0"++ "symbol" "1"++ }++ }++ "Trebuchet24"++ {++ "1"++ {++ "name" "GorDIN"++ "tall" "24"++ "weight" "700"++ "antialias" "1"++ "additive" "1"++ }++ }++ "Trebuchet18"++ {++ "1"++ {++ "name" "GorDIN"++ "tall" "18"++ "weight" "700"++ "antialias" "1"++ }++ }++ ClientTitleFont++ {++ "1"++ {++ "name" "HL2MP"++ "tall" "46"++ "weight" "0"++ "additive" "0"++ "antialias" "1"++ "custom" "1"++ }++ }++++ "ChatFont"++ {++ "1"++ {++ "name" "Verdana"++ "tall" "12"++ "weight" "700"++ "yres" "480 599"++ "dropshadow" "1"++ }++ "2"++ {++ "name" "Verdana"++ "tall" "13"++ "weight" "700"++ "yres" "600 767"++ "dropshadow" "1"++ }++ "3"++ {++ "name" "Verdana"++ "tall" "14"++ "weight" "700"++ "yres" "768 1023"++ "dropshadow" "1"++ }++ "4"++ {++ "name" "Verdana"++ "tall" "20"++ "weight" "700"++ "yres" "1024 1199"++ "dropshadow" "1"++ }++ "5" // Misyl: Proportional++ {++ "name" "Verdana"++ "tall" "9"++ "weight" "700"++ "antialias" "1"++ }++ }++++ "TargetID"++ {++ "1"++ {++ "name" "GorDIN"++ "tall" "24"++ "weight" "0"++ "antialias" "1"++ "additive" "1"++ }++ }++++ "HudNumbersTimer"++ {++ "1"++ {++ "name" "GorDIN"++ "tall" "15"++ "weight" "0"++ "antialias" "1"++ "additive" "1"++ }++ }++++ "HL2MPTypeDeath"++ {++ "1"++ {++ "name" "HL2MP" // csd.ttf++ "tall" "32"++ "weight" "0"++ "additive" "1"++ "antialias" "1"++ "custom" "1"++ }++ }++++ // Used by scoreboard and spectator UI for names which don't map in the normal fashion++ "DefaultVerySmallFallBack"++ {++ "1"++ {++ "name" "GorDIN"++ "tall" "10"++ "weight" "0"++ "range" "0x0000 0x017F" // Basic Latin, Latin-1 Supplement, Latin Extended-A++ "yres" "480 599"++ "antialias" "1"++ }++ "2"++ {++ "name" "GorDIN"++ "tall" "12"++ "weight" "0"++ "range" "0x0000 0x017F" // Basic Latin, Latin-1 Supplement, Latin Extended-A++ "yres" "600 1199"++ "antialias" "1"++ }++ "3"++ {++ "name" "GorDIN"++ "tall" "15"++ "weight" "0"++ "range" "0x0000 0x017F" // Basic Latin, Latin-1 Supplement, Latin Extended-A++ "yres" "1200 6000"++ "antialias" "1"++ }++ "4" // Proportional - misyl++ {++ "name" "GorDIN"++ "tall" "10"++ "weight" "0"++ "antialias" "1"++ }++ }++++ CreditsLogo++ {++ "1"++ {++ "name" "HalfLife2"++ "tall" "34"++ "weight" "0"++ "antialias" "1"++ "additive" "1"++ "custom" "1"++ }++ }++ CreditsIcons++ {++ "1"++ {++ "name" "HalfLife2"++ "tall" "34"++ "weight" "0"++ "antialias" "1"++ "additive" "1"++ "custom" "1"++ }++ }++ CreditsText++ {++ "1"++ {++ "name" "Trebuchet MS"++ "tall" "20"++ "weight" "900"++ "antialias" "1"++ "additive" "1"++ "yres" "480 899"++ }++ "2"++ {++ "name" "Trebuchet MS"++ "tall" "12"++ "weight" "900"++ "antialias" "1"++ "additive" "1"++ }++ }++ CreditsOutroLogos++ {++ "1"++ {++ "name" "HalfLife2"++ "tall" "34"++ "weight" "0"++ "antialias" "1"++ "additive" "1"++ "custom" "1"++ }++ }++ CreditsOutroValve++ {++ "1"++ {++ "name" "HalfLife2"++ "tall" "48"++ "weight" "0"++ "antialias" "1"++ "additive" "1"++ "custom" "1"++ }++ }++ CreditsOutroText++ {++ "1"++ {++ "name" "Verdana"++ "name" "Courier Bold"++ "tall" "16"++ "weight" "900"++ "antialias" "1"++ }++ }++ CenterPrintText++ {++ // note that this scales with the screen resolution++ "1"++ {++ "name" "GorDIN"++ "tall" "18"++ "weight" "700"++ "antialias" "1"++ "additive" "1"++ }++ }++ HDRDemoText++ {++ // note that this scales with the screen resolution++ "1"++ {++ "name" "Trebuchet MS"++ "tall" "24"++ "weight" "900"++ "antialias" "1"++ "additive" "1"++ }++ }++ "AchievementNotification"++ {++ "1"++ {++ "name" "GorDIN"++ "tall" "14"++ "weight" "900"++ "antialias" "1"++ }++ }++ "CommentaryDefault"++ {++ "1"++ {++ "name" "GorDIN"++ "tall" "12"++ "weight" "700"++ "yres" "480 599"++ "antialias" "1"++ }++ "2"++ {++ "name" "GorDIN"++ "tall" "13"++ "tall" "20"++ "weight" "700"++ "yres" "600 767"++ "antialias" "1"++ }++ "3"++ {++ "name" "GorDIN"++ "tall" "14"++ "weight" "700"++ "yres" "768 1023"++ "antialias" "1"++ }++ "4"++ {++ "name" "GorDIN"++ "tall" "20"++ "weight" "700"++ "yres" "1024 1199"++ "antialias" "1"++ }++ "6" // Proportional++ {++ "name" "GorDIN"++ "tall" "10"++ "weight" "700"++ "antialias" "1"++ }++++ }++ "SteamDeckLoadingText"++ {++ "7"++ {++ "name" "GorDIN"++ "tall" "24"++ "weight" "700"++ "antialias" "1"++ }++ }++++ "PDump"++ {++ "1"++ {++ "name" "GorDIN"++ "tall" "24"++ "weight" "400"++ "yres" "480 599"++ "antialias" "1"++ }++ "2"++ {++ "name" "GorDIN"++ "tall" "24"++ "weight" "400"++ "yres" "600 767"++ "antialias" "1"++ }++ "3"++ {++ "name" "GorDIN"++ "tall" "24"++ "weight" "400"++ "yres" "768 1023"++ "antialias" "1"++ }++ "4"++ {++ "name" "GorDIN"++ "tall" "24"++ "weight" "400"++ "yres" "1024 1199"++ "antialias" "1"++ }++ "5"++ {++ "name" "GorDIN"++ "tall" "40"++ "weight" "400"++ "yres" "1200 10000"++ "antialias" "1"++ }++ }++ "PDumpSmall"++ {++ "1"++ {++ "name" "GorDIN"++ "tall" "12"++ "weight" "400"++ "yres" "480 599"++ "antialias" "1"++ }++ "2"++ {++ "name" "GorDIN"++ "tall" "14"++ "weight" "400"++ "yres" "600 767"++ "antialias" "1"++ }++ "3"++ {++ "name" "GorDIN"++ "tall" "18"++ "weight" "400"++ "yres" "768 1023"++ "antialias" "1"++ }++ "4"++ {++ "name" "GorDIN"++ "tall" "20"++ "weight" "400"++ "yres" "1024 1199"++ "antialias" "1"++ }++ "5"++ {++ "name" "GorDIN"++ "tall" "35"++ "weight" "400"++ "yres" "1200 10000"++ "antialias" "1"++ }++ }++ "PDumpVerySmall"++ {++ "1"++ {++ "name" "GorDIN"++ "tall" "14"++ "weight" "400"++ "yres" "480 599"++ "antialias" "1"++ }++ "2"++ {++ "name" "GorDIN"++ "tall" "15"++ "weight" "400"++ "yres" "600 767"++ "antialias" "1"++ }++ "3"++ {++ "name" "GorDIN"++ "tall" "16"++ "weight" "400"++ "yres" "768 1023"++ "antialias" "1"++ }++ "4"++ {++ "name" "GorDIN"++ "tall" "20"++ "weight" "400"++ "yres" "1024 1199"++ "antialias" "1"++ }++ "5"++ {++ "name" "GorDIN"++ "tall" "30"++ "weight" "400"++ "yres" "1200 10000"++ "antialias" "1"++ }++ }++ }++++++ //////////////////////// CUSTOM FONT FILES /////////////////////////////++ //++ // specifies all the custom (non-system) font files that need to be loaded to service the above described fonts++ CustomFontFiles++ {++ "1" "resource/HALFLIFE2.ttf"++ "2" "resource/HL2crosshairs.ttf"++ "3" "resource/HL2MP.ttf"++ "4" "resource/linux_fonts/DejaVuSans.ttf"++ "5" "resource/linux_fonts/DejaVuSans-Bold.ttf"++ "6" "resource/linux_fonts/DejaVuSans-BoldOblique.ttf"++ "7" "resource/linux_fonts/DejaVuSans-Oblique.ttf"++ "8" "resource/linux_fonts/LiberationSans-Regular.ttf"++ "9" "resource/linux_fonts/LiberationSans-Bold.ttf"++ "10" "resource/linux_fonts/LiberationMono-Regular.ttf"++++ "11" "resource/GorDIN-Regular.ttf"++ "12" "resource/GorDIN-Bold.ttf"++ "13" "resource/GorDIN-Semibold.ttf"++ "14" "resource/GorDIN-Black.ttf"++ "15" "resource/GorDIN-Light.ttf"++ }++++}@@@ -2,38 +2,38 @@{"1"{-- "label" "OpenVibe Hub"-- "command" "engine openvibe_menu"++ "label" "OpenVibe.Games"++ "command" "engine echo OpenVibe.Games""OnlyInGame" "0"}"2"{"label" "Enter Hub"-- "command" "engine ov_join hub"++ "command" "engine connect 127.0.0.1:27015""OnlyInGame" "0"}"3"{"label" "Prop Hunt"-- "command" "engine ov_join prophunt"++ "command" "engine connect 127.0.0.2:27016""OnlyInGame" "0"}"4"{"label" "Deathrun"-- "command" "engine ov_join deathrun"++ "command" "engine connect 127.0.0.3:27017""OnlyInGame" "0"}"5"{"label" "Fort Wars"-- "command" "engine ov_join fortwars"++ "command" "engine connect 127.0.0.4:27018""OnlyInGame" "0"}"6"{"label" "Traitor Town"-- "command" "engine ov_join traitortown"++ "command" "engine connect 127.0.0.5:27019""OnlyInGame" "0"}"7"nnew file mode 100644BBinary files /dev/null and b/game/openvibe.games/resource/gordin-black.ttf differnnew file mode 100644BBinary files /dev/null and b/game/openvibe.games/resource/gordin-bold.ttf differnnew file mode 100644BBinary files /dev/null and b/game/openvibe.games/resource/gordin-light.ttf differnnew file mode 100644BBinary files /dev/null and b/game/openvibe.games/resource/gordin-regular.ttf differnnew file mode 100644BBinary files /dev/null and b/game/openvibe.games/resource/gordin-semibold.ttf differnnew file mode 100644@@@ -0,0 +1,90 @@++"lang"++{++ "Language" "English"++ "Tokens"++ {++ // Game title++ "OPENVIBE_GAME_TITLE" "OpenVibe: Source"++ "OPENVIBE_DOMAIN" "openvibe.games"++++ // Hub++ "OV_HUB_WELCOME" "Welcome to OpenVibe Hub! Step on a portal pad to join a game."++ "OV_HUB_PORTAL_PROPHUNT" "Prop Hunt Portal"++ "OV_HUB_PORTAL_DEATHRUN" "Deathrun Portal"++ "OV_HUB_PORTAL_FORTWARS" "Fort Wars Portal"++ "OV_HUB_PORTAL_TRAITORTOWN" "Traitor Town Portal"++ "OV_HUB_SHOP_NPC" "Models Shop"++ "OV_HUB_TRAILS_NPC" "Trails Shop"++ "OV_HUB_INVENTORY" "Your Inventory"++++ // Shop++ "OV_SHOP_TITLE" "OpenVibe Shop"++ "OV_SHOP_BALANCE" "Balance: %coins% VibeCoins"++ "OV_SHOP_BUY" "Buy"++ "OV_SHOP_EQUIP" "Equip"++ "OV_SHOP_OWNED" "Owned"++ "OV_SHOP_INSUFFICIENT" "Not enough VibeCoins!"++ "OV_SHOP_PURCHASED" "Purchased: %item%"++ "OV_SHOP_EQUIPPED" "Now using: %item%"++++ // Currency++ "OV_CURRENCY_NAME" "VibeCoins"++ "OV_CURRENCY_EARNED" "+%amount% VibeCoins earned!"++++ // Round phases++ "OV_PHASE_WAITING" "Waiting for players…"++ "OV_PHASE_STARTING" "Round starting in %seconds%s…"++ "OV_PHASE_ACTIVE" "%seconds%s remaining"++ "OV_PHASE_ENDED" "Round over!"++ "OV_MIN_PLAYERS" "Need %min% players to start (%current% connected)"++++ // Prop Hunt++ "OV_PH_TITLE" "Prop Hunt"++ "OV_PH_ROLE_HUNTER" "You are a HUNTER — find and eliminate the props!"++ "OV_PH_ROLE_PROP" "You are a PROP — hide before the hunters are released!"++ "OV_PH_HIDE_PHASE" "Props are hiding — %seconds%s"++ "OV_PH_HUNT_PHASE" "Hunt has begun! %props% props remaining — %seconds%s"++ "OV_PH_WIN_HUNTERS" "Hunters win! All props eliminated."++ "OV_PH_WIN_PROPS" "Props win! Survived the hunt."++++ // Deathrun++ "OV_DR_TITLE" "Deathrun"++ "OV_DR_ROLE_RUNNER" "You are a RUNNER — reach the end!"++ "OV_DR_ROLE_ACTIVATOR" "You are the ACTIVATOR — stop the runners!"++ "OV_DR_WIN_RUNNERS" "Runners win! %finished% made it to the end."++ "OV_DR_WIN_ACTIVATOR" "Activator wins! All runners eliminated."++++ // Fort Wars++ "OV_FW_TITLE" "Fort Wars"++ "OV_FW_TEAM_A" "Red Team"++ "OV_FW_TEAM_B" "Blue Team"++ "OV_FW_BUILD_PHASE" "BUILD PHASE — %seconds%s | Fortify your base!"++ "OV_FW_COMBAT_PHASE" "COMBAT PHASE — %seconds%s | Eliminate the enemy!"++ "OV_FW_WIN_A" "Red Team wins!"++ "OV_FW_WIN_B" "Blue Team wins!"++ "OV_FW_DRAW" "Draw!"++++ // Traitor Town++ "OV_TT_TITLE" "Traitor Town"++ "OV_TT_ROLE_INNOCENT" "You are INNOCENT — find and eliminate the traitors!"++ "OV_TT_ROLE_TRAITOR" "You are a TRAITOR — eliminate all innocents without being caught!"++ "OV_TT_ROLE_DETECTIVE" "You are the DETECTIVE — investigate and protect the innocents!"++ "OV_TT_WIN_INNOCENTS" "Innocents win! All traitors eliminated."++ "OV_TT_WIN_TRAITORS" "Traitors win! All innocents eliminated."++ "OV_TT_TRAITOR_REVEAL" "Traitor: %name%"++++ // Item types++ "OV_ITEM_TYPE_PLAYER_MODEL" "Player Model"++ "OV_ITEM_TYPE_TRAIL" "Trail"++ "OV_ITEM_TYPE_NAMEPLATE" "Nameplate"++++ // XP++ "OV_XP_LABEL" "XP"++ "OV_XP_GAINED" "+%xp% XP"++++ // Generic++ "OV_LOADING" "Connecting to server…"++ "OV_DISCONNECT" "Disconnected from server."++ "OV_SERVER_FULL" "Server is full. Try again shortly."++ }++}nnew file mode 100644@@@ -0,0 +1,861 @@++// sample animation script++//++//++// commands:++// Animate <panel name> <variable> <target value> <interpolator> <start time> <duration>++// variables:++// FgColor++// BgColor++// Position++// Size++// Blur (hud panels only)++// TextColor (hud panels only)++// Ammo2Color (hud panels only)++// Alpha (hud weapon selection only)++// SelectionAlpha (hud weapon selection only)++// TextScan (hud weapon selection only)++//++// interpolator:++// Linear++// Accel - starts moving slow, ends fast++// Deaccel - starts moving fast, ends slow++//++// RunEvent <event name> <start time>++// starts another even running at the specified time++//++// StopEvent <event name> <start time>++// stops another event that is current running at the specified time++//++// StopAnimation <panel name> <variable> <start time>++// stops all animations refering to the specified variable in the specified panel++//++// StopPanelAnimations <panel name> <start time>++// stops all active animations operating on the specified panel++//++//++// Useful game console commands:++// cl_Animationinfo <hudelement name> or <panelname>++// displays all the animatable variables for the hud element++//++++++event LevelInit++{++}++++event FadeOutTeamLine++{++ // make the display visible++ Animate TeamDisplay Alpha "0" Linear 0.0 0.25++}++++event FadeInTeamLine++{++ // make the display visible++ Animate TeamDisplay Alpha "255" Linear 0.0 0.5++}++++event WeaponHighlight++{++ Animate HudWeaponSelection Blur "7" Linear 0.0 0.0++ Animate HudWeaponSelection Blur "0" Deaccel 0.01 0.75++++ Animate HudWeaponSelection SelectedBoxColor "250 220 0 80" Linear 0.0 0.01++ Animate HudWeaponSelection SelectedBoxColor "BgColor" Deaccel 0.1 1.5++ Animate HudWeaponSelection SelectedFgColor "BrightFg" Linear 0.0 0.1++ Animate HudWeaponSelection SelectedFgColor "FgColor" Linear 5.0 1.5++}++++event OpenWeaponSelectionMenu++{++ StopEvent CloseWeaponSelectionMenu 0.0++ StopEvent WeaponPickup 0.0++ StopEvent FadeOutWeaponSelectionMenu 0.0++++ // make the display visible++ Animate HudWeaponSelection Alpha "128" Linear 0.0 0.1++ Animate HudWeaponSelection Alpha "192" Linear 0.0 0.1++ Animate HudWeaponSelection SelectionAlpha "255" Linear 0.0 0.1++ Animate HudWeaponSelection FgColor "FgColor" Linear 0.0 0.1++ Animate HudWeaponSelection TextColor "BrightFg" Linear 0.0 0.1++ Animate HudWeaponSelection TextScan "1" Linear 0.0 0.1++}++++event CloseWeaponSelectionMenu++{++ // hide the whole thing near immediately++ Animate HudWeaponSelection FgColor "0 0 0 0" Linear 0.0 0.1++ Animate HudWeaponSelection TextColor "0 0 0 0" Linear 0.0 0.1++ Animate HudWeaponSelection Alpha "0" Linear 0.0 0.1++ Animate HudWeaponSelection SelectionAlpha "0" Linear 0.0 0.1++}++++event FadeOutWeaponSelectionMenu++{++ // slowly hide the whole thing++ Animate HudWeaponSelection FgColor "0 0 0 0" Linear 0.5 1.0++ Animate HudWeaponSelection TextColor "0 0 0 0" Linear 0.5 1.0++ Animate HudWeaponSelection Alpha "0" Linear 0.5 1.0++ Animate HudWeaponSelection SelectionAlpha "0" Linear 0.5 1.0++++ Animate HudWeaponSelection WeaponBoxDist1Alpha "0 0 0 0" Linear 0.0 0.5++ Animate HudWeaponSelection WeaponBoxDist2Alpha "0 0 0 0" Linear 0.0 0.5++ Animate HudWeaponSelection WeaponBoxDist3Alpha "0 0 0 0" Linear 0.0 0.5++}++++event SuitAuxPowerMax++{++ // hide the suit power++ Animate HudSuitPower BgColor "0 0 0 0" Linear 0.0 0.4++ Animate HudSuitPower AuxPowerColor "0 0 0 0" Linear 0.0 0.4++}++++event SuitAuxPowerNotMax++{++ // show suit power++ Animate HudSuitPower BgColor "BgColor" Linear 0.0 0.4++ Animate HudSuitPower AuxPowerColor "255 220 0 220" Linear 0.0 0.4++}++++event SuitAuxPowerDecreasedBelow25++{++ // make color red++ Animate HudSuitPower AuxPowerColor "255 0 0 220" Linear 0.0 0.4++}++++event SuitAuxPowerIncreasedAbove25++{++ // make colr bright++ Animate HudSuitPower AuxPowerColor "255 220 0 220" Linear 0.0 0.4++}++++event SuitAuxPowerNoItemsActive++{++ // resize the aux power to be the smallest size++ Animate HudSuitPower Size "102 26" Linear 0.0 0.4++ Animate HudSuitPower Position "16 400" Linear 0.0 0.4++ Animate HudSuitPower Size "112 30" Linear 0.0 0.4++ Animate HudSuitPower Position "16 390" Linear 0.0 0.4++}++++event SuitAuxPowerOneItemActive++{++ // resize the aux power to fit one item++ Animate HudSuitPower Size "102 36" Linear 0.0 0.4++ Animate HudSuitPower Position "16 390" Linear 0.0 0.4++ Animate HudSuitPower Size "112 44" Linear 0.0 0.4++ Animate HudSuitPower Position "16 372" Linear 0.0 0.4++}++++event SuitAuxPowerTwoItemsActive++{++ // resize the aux power to fit two items++ Animate HudSuitPower Size "102 46" Linear 0.0 0.4++ Animate HudSuitPower Position "16 380" Linear 0.0 0.4++ Animate HudSuitPower Size "112 56" Linear 0.0 0.4++ Animate HudSuitPower Position "16 364" Linear 0.0 0.4++}++++event SuitAuxPowerThreeItemsActive++{++ // resize the aux power to fit three items++ Animate HudSuitPower Size "102 56" Linear 0.0 0.4++ Animate HudSuitPower Position "16 370" Linear 0.0 0.4++ Animate HudSuitPower Size "112 70" Linear 0.0 0.4++ Animate HudSuitPower Position "16 350" Linear 0.0 0.4++}++++event SuitFlashlightOn++{++ Animate HudFlashlight TextColor "255 220 0 255" Linear 0.0 0.1++ Animate HudFlashlight TextColor "255 220 0 160" Deaccel 0.1 0.75++ Animate HudFlashlight BgColor "BgColor" Linear 0.0 0.75++}++++event SuitFlashlightOff++{++ StopEvent SuitFlashlightOn 0.0++ Animate HudFlashlight TextColor "0 0 0 0" Linear 0.0 0.4++ Animate HudFlashlight BgColor "0 0 0 0" Linear 0.0 0.4++}++++event HudTakeDamageFront++{++}++++event HudTakeDamageLeft++{++ Animate HudDamageIndicator DmgColorLeft "255 88 0 200" Linear 0.0 0.0++ Animate HudDamageIndicator DmgColorLeft "255 0 0 200" Linear 0.0 0.3++ Animate HudDamageIndicator DmgColorLeft "255 0 0 0" Deaccel 0.3 0.5++}++++event HudTakeDamageRight++{++ Animate HudDamageIndicator DmgColorRight "255 88 0 200" Linear 0.0 0.0++ Animate HudDamageIndicator DmgColorRight "255 0 0 200" Linear 0.0 0.3++ Animate HudDamageIndicator DmgColorRight "255 0 0 0" Deaccel 0.3 0.5++}++++event HudTakeDamageBehind++{++ RunEvent HudTakeDamageLeft 0.0++ RunEvent HudTakeDamageRight 0.0++}++++event HudTakeDamageHighLeft++{++ Animate HudDamageIndicator DmgHighColorLeft "255 88 0 255" Linear 0.0 0.0++ Animate HudDamageIndicator DmgHighColorLeft "255 0 0 200" Linear 0.0 0.4++ Animate HudDamageIndicator DmgHighColorLeft "255 0 0 0" Deaccel 0.4 2.4++}++++event HudTakeDamageHighRight++{++ Animate HudDamageIndicator DmgHighColorRight "255 88 0 255" Linear 0.0 0.0++ Animate HudDamageIndicator DmgHighColorRight "255 0 0 200" Linear 0.0 0.4++ Animate HudDamageIndicator DmgHighColorRight "255 0 0 0" Deaccel 0.4 2.4++}++++event HudTakeDamageHigh++{++ Animate HudDamageIndicator DmgFullscreenColor "255 88 0 200" Linear 0.0 0.0++ Animate HudDamageIndicator DmgFullscreenColor "255 0 0 200" Linear 0.0 0.4++ Animate HudDamageIndicator DmgFullscreenColor "255 0 0 0" Deaccel 0.4 2.4++}++++event HudTakeDamageDrown++{++ RunEvent HudTakeDamageBehind 0.0++}++++event HudTakeDamagePoison++{++ Animate HudDamageIndicator DmgFullscreenColor "255 236 128 240" Linear 0.0 0.0++ Animate HudDamageIndicator DmgFullscreenColor "255 236 128 0" Deaccel 0.4 0.8++}++++event HudTakeDamageBurn++{++ Animate HudDamageIndicator DmgFullscreenColor "255 0 0 200" Linear 0.0 0.0++ Animate HudDamageIndicator DmgFullscreenColor "255 0 0 0" Deaccel 0.2 0.4++}++++event HudTakeDamageRadiation++{++ Animate HudDamageIndicator DmgFullscreenColor "255 255 255 128" Deaccel 0.0 0.1++ Animate HudDamageIndicator DmgFullscreenColor "255 255 255 0" Deaccel 0.1 0.4++ RunEvent HudTakeDamageBehind 0.0++}++++event HudPlayerDeath++{++ StopEvent HealthLoop 0.0++ StopEvent HealthPulse 0.0++++ Animate HudDamageIndicator DmgFullscreenColor "255 0 0 96" Deaccel 0.1 0.2++ Animate HudDamageIndicator DmgFullscreenColor "255 0 0 255" Deaccel 0.3 4.0++}++++event HealthIncreasedAbove20++{++ StopEvent HealthLoop 0.0++ StopEvent HealthPulse 0.0++ StopEvent HealthLow 0.0++++ Animate HudHealth BgColor "BgColor" Linear 0.0 0.0++++ Animate HudHealth TextColor "FgColor" Linear 0.0 0.04++ Animate HudHealth FgColor "FgColor" Linear 0.0 0.03++++ Animate HudHealth Blur "3" Linear 0.0 0.1++ Animate HudHealth Blur "0" Deaccel 0.1 2.0++}++++event HealthIncreasedBelow20++{++ Animate HudHealth FgColor "BrightFg" Linear 0.0 0.25++ Animate HudHealth FgColor "FgColor" Linear 0.3 0.75++++ Animate HudHealth Blur "3" Linear 0.0 0.1++ Animate HudHealth Blur "0" Deaccel 0.1 2.0++}++++event SuitPowerIncreasedAbove20++{++ StopEvent SuitLoop 0.0++ StopEvent SuitPulse 0.0++ StopEvent SuitPowerZero 0.0++++ Animate HudSuit Alpha "255" Linear 0.0 0.0++++ Animate HudSuit BgColor "BgColor" Linear 0.0 0.0++++ Animate HudSuit TextColor "FgColor" Linear 0.0 0.05++ Animate HudSuit FgColor "FgColor" Linear 0.0 0.05++++ Animate HudSuit Blur "3" Linear 0.0 0.1++ Animate HudSuit Blur "0" Deaccel 0.1 2.0++}++++++event SuitPowerIncreasedBelow20++{++ StopEvent SuitPowerZero 0.0++ Animate HudSuit Alpha "255" Linear 0.0 0.0++++ Animate HudSuit FgColor "BrightFg" Linear 0.0 0.25++ Animate HudSuit FgColor "FgColor" Linear 0.3 0.75++++ Animate HudSuit Blur "3" Linear 0.0 0.1++ Animate HudSuit Blur "0" Deaccel 0.1 2.0++}++++event SuitPowerZero++{++ StopEvent SuitLoop 0.0++ StopEvent SuitPulse 0.0++ StopEvent SuitArmorLow 0.0++ StopEvent SuitDamageTaken 0.0++++ Animate HudSuit Alpha "0" Linear 0.0 0.4++}++++event TestMovement++{++ Animate HudHealth Position "256 120" Linear 0.0 2.0++ Animate HudHealth Size "128 24" Linear 0.0 1.5++}++++event HealthDamageTaken++{++ Animate HudHealth FgColor "BrightFg" Linear 0.0 0.25++ Animate HudHealth FgColor "FgColor" Linear 0.3 0.75++++ Animate HudHealth Blur "3" Linear 0.0 0.1++ Animate HudHealth Blur "0" Deaccel 0.1 2.0++++ Animate HudHealth TextColor "BrightFg" Linear 0.0 0.1++ Animate HudHealth TextColor "FgColor" Deaccel 0.1 1.2++}++++event SuitDamageTaken++{++ Animate HudSuit FgColor "BrightFg" Linear 0.0 0.25++ Animate HudSuit FgColor "FgColor" Linear 0.3 0.75++++ Animate HudSuit Blur "3" Linear 0.0 0.1++ Animate HudSuit Blur "0" Deaccel 0.1 2.0++++ Animate HudSuit TextColor "BrightFg" Linear 0.0 0.1++ Animate HudSuit TextColor "FgColor" Deaccel 0.1 1.2++}++++// health has been damaged to below 20%++event HealthLow++{++ StopEvent HealthDamageTaken 0.0++ StopEvent HealthPulse 0.0++ StopEvent HealthLoop 0.0++++ Animate HudHealth BgColor "DamagedBg" Linear 0.0 0.1++ Animate HudHealth BgColor "BgColor" Deaccel 0.1 1.75++++ Animate HudHealth FgColor "BrightFg" Linear 0.0 0.2++ Animate HudHealth FgColor "DamagedFg" Linear 0.2 1.2++++ Animate HudHealth TextColor "BrightFg" Linear 0.0 0.1++ Animate HudHealth TextColor "DamagedFg" Linear 0.1 1.2++++ Animate HudHealth Blur "5" Linear 0.0 0.1++ Animate HudHealth Blur "3" Deaccel 0.1 0.9++++ RunEvent HealthPulse 1.0++}++++event HealthPulse++{++ Animate HudHealth Blur "5" Linear 0.0 0.1++ Animate HudHealth Blur "2" Deaccel 0.1 0.8++ Animate HudHealth TextColor "BrightDamagedFg" Linear 0.0 0.1++ Animate HudHealth TextColor "DamagedFg" Deaccel 0.1 0.8++ Animate HudHealth BgColor "100 0 0 80" Linear 0.0 0.1++ Animate HudHealth BgColor "BgColor" Deaccel 0.1 0.8++++ RunEvent HealthLoop 0.8++}++++// call to loop HealthPulse++event HealthLoop++{++ RunEvent HealthPulse 0.0++}++++++// suit armor has been damaged to below 20%++event SuitArmorLow++{++ StopEvent SuitDamageTaken 0.0++ StopEvent SuitPulse 0.0++ StopEvent SuitLoop 0.0++++// removing this effect -- matching it to the event for normal suit damage++// so, there will be no special indication (redness, flashing)++// in the hud that the suit armor is low++++// Animate HudSuit BgColor "DamagedBg" Linear 0.0 0.1++// Animate HudSuit BgColor "BgColor" Deaccel 0.1 1.75++++ Animate HudSuit FgColor "BrightFg" Linear 0.0 0.25++ Animate HudSuit FgColor "FgColor" Linear 0.3 0.75++++ Animate HudSuit Blur "3" Linear 0.0 0.1++ Animate HudSuit Blur "0" Deaccel 0.1 2.0++++ Animate HudSuit TextColor "BrightFg" Linear 0.0 0.1++ Animate HudSuit TextColor "FgColor" Deaccel 0.1 1.2++++// RunEvent SuitPulse 1.0++}++++event SuitPulse++// this even no longer gets called++{++ Animate HudSuit Blur "5" Linear 0.0 0.1++ Animate HudSuit Blur "2" Deaccel 0.1 0.8++ Animate HudSuit TextColor "BrightDamagedFg" Linear 0.0 0.1++ Animate HudSuit TextColor "DamagedFg" Deaccel 0.1 0.8++ Animate HudSuit BgColor "100 0 0 80" Linear 0.0 0.1++ Animate HudSuit BgColor "BgColor" Deaccel 0.1 0.8++ RunEvent SuitLoop 0.8++}++++event SuitLoop++{++// this event no longer gets called++ RunEvent SuitPulse 0.0++}++++// ammo has been picked up++event AmmoIncreased++{++ Animate HudAmmo FgColor "BrightFg" Linear 0.0 0.15++ Animate HudAmmo FgColor "FgColor" Deaccel 0.15 1.5++ Animate HudAmmo Blur "5" Linear 0.0 0.0++ Animate HudAmmo Blur "0" Accel 0.01 1.5++}++++// ammo has been decreased, but there is still some remaining++event AmmoDecreased++{++ StopEvent AmmoIncreased 0.0++++ Animate HudAmmo Blur "7" Linear 0.0 0.0++ Animate HudAmmo Blur "0" Deaccel 0.1 1.5++++ Animate HudAmmo TextColor "BrightFg" Linear 0.0 0.1++ Animate HudAmmo TextColor "FgColor" Deaccel 0.1 0.75++}++++// primary ammo is zero++event AmmoEmpty++{++ Animate Hudammo FgColor "BrightDamagedFg" Linear 0.0 0.2++ Animate Hudammo FgColor "DamagedFg" Accel 0.2 1.2++}++++// ammo2 is the total ammo for a weapon that uses clip ammo++event Ammo2Increased++{++ Animate Hudammo ammo2color "BrightFg" Linear 0.0 0.2++ Animate Hudammo ammo2color "FgColor" Accel 0.2 1.2++}++++// total ammo has been decreased, but there is still some remaining++event Ammo2Decreased++{++ Animate Hudammo ammo2color "BrightFg" Linear 0.0 0.2++ Animate Hudammo ammo2color "FgColor" Accel 0.2 1.2++}++++// total ammo is zero++event Ammo2Empty++{++ Animate Hudammo ammo2color "BrightDamagedFg" Linear 0.0 0.2++ Animate Hudammo ammo2color "DamagedFg" Accel 0.2 1.2++}++++event AmmoSecondaryIncreased++{++ Animate HudAmmoSecondary FgColor "BrightFg" Linear 0.0 0.15++ Animate HudAmmoSecondary FgColor "FgColor" Deaccel 0.15 1.5++ Animate HudAmmoSecondary Blur "5" Linear 0.0 0.0++ Animate HudAmmoSecondary Blur "0" Accel 0.01 1.5++}++++event AmmoSecondaryDecreased++{++ StopEvent AmmoSecondaryIncreased 0.0++++ Animate HudAmmoSecondary Blur "7" Linear 0.0 0.0++ Animate HudAmmoSecondary Blur "0" Deaccel 0.1 1.5++++ Animate HudAmmoSecondary TextColor "BrightFg" Linear 0.0 0.1++ Animate HudAmmoSecondary TextColor "FgColor" Deaccel 0.1 0.75++}++++event AmmoSecondaryEmpty++{++ Animate HudAmmoSecondary FgColor "BrightDamagedFg" Linear 0.0 0.2++ Animate HudAmmoSecondary FgColor "DamagedFg" Accel 0.2 1.2++ Animate HudAmmoSecondary Blur "7" Linear 0.0 0.0++ Animate HudAmmoSecondary Blur "0" Deaccel 0.1 1.5++++}++++// current weapon has been changed++event WeaponChanged++{++ Animate HudAmmo BgColor "250 220 0 80" Linear 0.0 0.1++ Animate HudAmmo BgColor "BgColor" Deaccel 0.1 1.0++ Animate HudAmmo FgColor "BrightFg" Linear 0.0 0.1++ Animate HudAmmo FgColor "FgColor" Linear 0.2 1.5++}++++// ran if we just changed to a weapon that needs clip ammo++event WeaponUsesClips++{++ Animate HudAmmo Position "r150 432" Deaccel 0.0 0.4++ Animate HudAmmo Size "132 36" Deaccel 0.0 0.4++}++event WeaponUsesClips++{++ Animate HudAmmo Position "r162 426" Deaccel 0.0 0.4++ Animate HudAmmo Size "144 42" Deaccel 0.0 0.4++}++++// ran if we just changed to a weapon that does not use clip ammo++event WeaponDoesNotUseClips++{++ Animate HudAmmo Position "r118 432" Deaccel 0.0 0.4++ Animate HudAmmo Size "100 36" Deaccel 0.0 0.4++}++event WeaponDoesNotUseClips++{++ Animate HudAmmo Position "r130 426" Deaccel 0.0 0.4++ Animate HudAmmo Size "120 42" Deaccel 0.0 0.4++}++++event WeaponUsesSecondaryAmmo++{++ StopAnimation HudAmmo Position 0.0++ StopAnimation HudAmmo Size 0.0++ StopPanelAnimations HudAmmoSecondary 0.0++ //StopPanelAnimations HudAmmo 0.0++++ Animate HudAmmoSecondary BgColor "250 220 0 60" Linear 0.0 0.1++ Animate HudAmmoSecondary BgColor "BgColor" Deaccel 0.1 1.0++ Animate HudAmmoSecondary FgColor "BrightFg" Linear 0.0 0.1++ Animate HudAmmoSecondary FgColor "FgColor" Linear 0.2 1.5++ Animate HudAmmoSecondary Alpha 255 Linear 0.0 0.1++++ Animate HudAmmo Position "r222 432" Deaccel 0.0 0.5++ Animate HudAmmo Size "132 36" Deaccel 0.0 0.4++ Animate HudAmmo Position "r234 426" Deaccel 0.0 0.5++ Animate HudAmmo Size "144 42" Deaccel 0.0 0.4++}++++event WeaponDoesNotUseSecondaryAmmo++{++ StopPanelAnimations HudAmmoSecondary 0.0++ Animate HudAmmoSecondary FgColor "0 0 0 0" Linear 0.0 0.4++ Animate HudAmmoSecondary BgColor "0 0 0 0" Linear 0.0 0.4++ Animate HudAmmoSecondary Alpha 0 Linear 0.0 0.1++}++++event CraneMagnetFlash++{++ Animate HudCraneMagnet TextColor "255 220 0 255" Linear 0.0 0.1++ Animate HudCraneMagnet TextColor "255 220 0 160" Deaccel 0.1 0.3++ Animate HudCraneMagnet TextColor "255 170 0 220" Deaccel 0.4 0.3++ Animate HudCraneMagnet TextColor "255 220 0 255" Linear 0.8 0.2++}++++event HintMessageShow++{++ StopPanelAnimations HudHintDisplay 0.0++++ Animate HudHintDisplay HintSize "1" Deaccel 0.0 0.3++ Animate HudHintDisplay FgColor "FgColor" Linear 0.4 0.4++++ // flash text++ Animate HudHintDisplay FgColor "FgColor" Linear 1.5 0.01++ Animate HudHintDisplay FgColor "255 220 0 255" Linear 2.0 0.2++ Animate HudHintDisplay FgColor "FgColor" Linear 2.2 0.2++ Animate HudHintDisplay FgColor "255 220 0 255" Linear 3.0 0.2++ Animate HudHintDisplay FgColor "FgColor" Linear 3.2 0.2++++ // hide the panel after a while++ Animate HudHintDisplay FgColor "255 220 0 0" Linear 10.0 0.2++ Animate HudHintDisplay HintSize "0" Deaccel 10.2 0.3++}++++++event HintMessageHide++{++ Animate HudHintDisplay FgColor "255 220 0 0" Linear 0.0 0.2++ Animate HudHintDisplay HintSize "0" Deaccel 0.2 0.3++}++++event KeyHintMessageShow++{++ StopPanelAnimations HudHintKeyDisplay 0.0++++ // show the hints++ Animate HudHintKeyDisplay Alpha 255 Linear 0.0 0.5++++ // flash text++ Animate HudHintKeyDisplay FgColor "FgColor" Linear 0.0 0.01++ Animate HudHintKeyDisplay FgColor "255 220 0 255" Linear 0.5 0.2++ Animate HudHintKeyDisplay FgColor "FgColor" Linear 0.7 0.2++ Animate HudHintKeyDisplay FgColor "255 220 0 255" Linear 1.5 0.2++ Animate HudHintKeyDisplay FgColor "FgColor" Linear 1.7 0.2++++ // hide the panel after a while++ Animate HudHintKeyDisplay Alpha 0 Linear 12.0 1.0++}++++event KeyHintMessageHide++{++ Animate HudHintKeyDisplay Alpha 0 Linear 0.0 0.5++}++++++event SquadMemberAdded++{++ StopEvent SquadMemberDied 0.0++ StopEvent SquadMemberLeft 0.0++++ // add in the squad member, brighter then normal color++ Animate HudSquadStatus LastMemberColor "255 220 0 255" Linear 0.0 0.3++ Animate HudSquadStatus LastMemberColor "255 220 0 160" Linear 0.3 0.3++}++++event SquadMemberLeft++{++ StopEvent SquadMemberDied 0.0++ StopEvent SquadMemberAdded 0.0++++ // fade out the icon++ Animate HudSquadStatus LastMemberColor "255 220 0 0" Linear 0.0 0.5++}++++event SquadMemberDied++{++ StopEvent SquadMemberAdded 0.0++ StopEvent SquadMemberLeft 0.0++++ // flash red, hold, then disappear++ Animate HudSquadStatus LastMemberColor "255 0 0 255" Linear 0.0 0.5++ Animate HudSquadStatus LastMemberColor "255 0 0 0" Linear 2.0 2.0++}++++event SquadMembersFollowing++{++ StopEvent SquadMembersStationed 0.0++ Animate HudSquadStatus SquadTextColor "255 220 0 255" Linear 0.0 0.2++ Animate HudSquadStatus SquadTextColor "255 220 0 160" Linear 0.2 0.4++}++++event SquadMembersStationed++{++ StopEvent SquadMembersFollowing 0.0++ Animate HudSquadStatus SquadTextColor "255 220 0 160" Linear 0.5 0.5++}++++event PoisonDamageTaken++{++ Animate HudPoisonDamageIndicator Alpha 255 Linear 0.0 1.0++ RunEvent PoisonLoop 0.0++}++++event PoisonDamageCured++{++ StopEvent PoisonDamageTaken 0.0++ StopEvent PoisonLoop 0.0++ StopEvent PoisonPulse 0.0++ Animate HudPoisonDamageIndicator Alpha 0 Linear 0.0 1.0++}++++event PoisonPulse++{++ Animate HudPoisonDamageIndicator TextColor "BrightFg" Linear 0.0 0.1++ Animate HudPoisonDamageIndicator TextColor "FgColor" Deaccel 0.1 0.8++ Animate HudPoisonDamageIndicator BgColor "100 0 0 80" Linear 0.0 0.1++ Animate HudPoisonDamageIndicator BgColor "BgColor" Deaccel 0.1 0.8++++ RunEvent PoisonLoop 0.8++}++++// call to loop PoisonLoop++event PoisonLoop++{++ RunEvent PoisonPulse 0.0++}++++// Commentary hud element++event ShowCommentary++{++ StopEvent HideCommentary 0.0++ Animate HudCommentary Alpha 255 Linear 0.0 0.5++}++++event HideCommentary++{++ StopEvent ShowCommentary 0.0++ Animate HudCommentary Alpha 0 Linear 0.0 0.5++}++++// Voting++event PulseOption1++{++ Animate Option2Background_Selected Alpha 0 Linear 0.0 0.001++ Animate Option3Background_Selected Alpha 0 Linear 0.0 0.001++ Animate Option4Background_Selected Alpha 0 Linear 0.0 0.001++ Animate Option5Background_Selected Alpha 0 Linear 0.0 0.001++++ Animate Option1Background_Selected Alpha 255 Linear 0.0 0.001++ Animate Option1Background_Selected Alpha 128 Linear 0.5 1.5++++ Animate YesLeftLabel Alpha 0 Linear 1.25 1.7++ Animate YesIcon Alpha 0 Linear 1.25 1.7++ Animate YesRightLabel Alpha 0 Linear 1.25 1.7++++ Animate NoLeftLabel Alpha 0 Linear 1.25 1.7++ Animate NoIcon Alpha 0 Linear 1.25 1.7++ Animate NoRightLabel Alpha 0 Linear 1.25 1.7++++ Animate LabelOption2 Alpha 60 Linear 1.25 1.7++ Animate LabelOption3 Alpha 60 Linear 1.25 1.7++ Animate LabelOption4 Alpha 60 Linear 1.25 1.7++ Animate LabelOption5 Alpha 60 Linear 1.25 1.7++}++++event PulseOption2++{++ Animate Option1Background_Selected Alpha 0 Linear 0.0 0.001++ Animate Option3Background_Selected Alpha 0 Linear 0.0 0.001++ Animate Option4Background_Selected Alpha 0 Linear 0.0 0.001++ Animate Option5Background_Selected Alpha 0 Linear 0.0 0.001++++ Animate Option2Background_Selected Alpha 255 Linear 0.0 0.001++ Animate Option2Background_Selected Alpha 128 Linear 0.5 1.5++++ Animate YesLeftLabel Alpha 0 Linear 1.25 1.7++ Animate YesIcon Alpha 0 Linear 1.25 1.7++ Animate YesRightLabel Alpha 0 Linear 1.25 1.7++++ Animate NoLeftLabel Alpha 0 Linear 1.25 1.7++ Animate NoIcon Alpha 0 Linear 1.25 1.7++ Animate NoRightLabel Alpha 0 Linear 1.25 1.7++++ Animate LabelOption1 Alpha 60 Linear 1.25 1.7++ Animate LabelOption3 Alpha 60 Linear 1.25 1.7++ Animate LabelOption4 Alpha 60 Linear 1.25 1.7++ Animate LabelOption5 Alpha 60 Linear 1.25 1.7++}++++event PulseOption3++{++ Animate Option1Background_Selected Alpha 0 Linear 0.0 0.001++ Animate Option2Background_Selected Alpha 0 Linear 0.0 0.001++ Animate Option4Background_Selected Alpha 0 Linear 0.0 0.001++ Animate Option5Background_Selected Alpha 0 Linear 0.0 0.001++++ Animate Option3Background_Selected Alpha 255 Linear 0.0 0.001++ Animate Option3Background_Selected Alpha 128 Linear 0.5 1.5++++ Animate YesLeftLabel Alpha 0 Linear 1.25 1.7++ Animate YesIcon Alpha 0 Linear 1.25 1.7++ Animate YesRightLabel Alpha 0 Linear 1.25 1.7++++ Animate NoLeftLabel Alpha 0 Linear 1.25 1.7++ Animate NoIcon Alpha 0 Linear 1.25 1.7++ Animate NoRightLabel Alpha 0 Linear 1.25 1.7++++ Animate LabelOption1 Alpha 60 Linear 1.25 1.7++ Animate LabelOption2 Alpha 60 Linear 1.25 1.7++ Animate LabelOption4 Alpha 60 Linear 1.25 1.7++ Animate LabelOption5 Alpha 60 Linear 1.25 1.7++}++++event PulseOption4++{++ Animate Option1Background_Selected Alpha 0 Linear 0.0 0.001++ Animate Option2Background_Selected Alpha 0 Linear 0.0 0.001++ Animate Option3Background_Selected Alpha 0 Linear 0.0 0.001++ Animate Option5Background_Selected Alpha 0 Linear 0.0 0.001++++ Animate Option4Background_Selected Alpha 255 Linear 0.0 0.001++ Animate Option4Background_Selected Alpha 128 Linear 0.5 1.5++++ Animate YesLeftLabel Alpha 0 Linear 1.25 1.7++ Animate YesIcon Alpha 0 Linear 1.25 1.7++ Animate YesRightLabel Alpha 0 Linear 1.25 1.7++++ Animate NoLeftLabel Alpha 0 Linear 1.25 1.7++ Animate NoIcon Alpha 0 Linear 1.25 1.7++ Animate NoRightLabel Alpha 0 Linear 1.25 1.7++++ Animate LabelOption1 Alpha 60 Linear 1.25 1.7++ Animate LabelOption2 Alpha 60 Linear 1.25 1.7++ Animate LabelOption3 Alpha 60 Linear 1.25 1.7++ Animate LabelOption5 Alpha 60 Linear 1.25 1.7++}++++event PulseOption5++{++ Animate Option1Background_Selected Alpha 0 Linear 0.0 0.001++ Animate Option2Background_Selected Alpha 0 Linear 0.0 0.001++ Animate Option3Background_Selected Alpha 0 Linear 0.0 0.001++ Animate Option4Background_Selected Alpha 0 Linear 0.0 0.001++++ Animate Option5Background_Selected Alpha 255 Linear 0.0 0.001++ Animate Option5Background_Selected Alpha 128 Linear 0.5 1.5++++ Animate YesLeftLabel Alpha 0 Linear 1.25 1.7++ Animate YesIcon Alpha 0 Linear 1.25 1.7++ Animate YesRightLabel Alpha 0 Linear 1.25 1.7++++ Animate NoLeftLabel Alpha 0 Linear 1.25 1.7++ Animate NoIcon Alpha 0 Linear 1.25 1.7++ Animate NoRightLabel Alpha 0 Linear 1.25 1.7++++ Animate LabelOption1 Alpha 60 Linear 1.25 1.7++ Animate LabelOption2 Alpha 60 Linear 1.25 1.7++ Animate LabelOption3 Alpha 60 Linear 1.25 1.7++ Animate LabelOption4 Alpha 60 Linear 1.25 1.7++}++++event HideVoteBackgrounds++{++ Animate Option1Background_Selected Alpha 0 Linear 0.0 0.001++ Animate Option2Background_Selected Alpha 0 Linear 0.0 0.001++ Animate Option3Background_Selected Alpha 0 Linear 0.0 0.001++ Animate Option4Background_Selected Alpha 0 Linear 0.0 0.001++ Animate Option5Background_Selected Alpha 0 Linear 0.0 0.001++++ Animate YesLeftLabel Alpha 255 Linear 0.0 0.001++ Animate YesIcon Alpha 255 Linear 0.0 0.001++ Animate YesRightLabel Alpha 255 Linear 0.0 0.001++++ Animate NoLeftLabel Alpha 255 Linear 0.0 0.001++ Animate NoIcon Alpha 255 Linear 0.0 0.001++ Animate NoRightLabel Alpha 255 Linear 0.0 0.001++++ Animate LabelOption1 Alpha 255 Linear 0.0 0.001++ Animate LabelOption2 Alpha 255 Linear 0.0 0.001++ Animate LabelOption3 Alpha 255 Linear 0.0 0.001++ Animate LabelOption4 Alpha 255 Linear 0.0 0.001++ Animate LabelOption5 Alpha 255 Linear 0.0 0.001++}nnew file mode 100644@@@ -0,0 +1,758 @@++"Resource/HudLayout.res"++{++ HudHealth++ {++ "fieldName" "HudHealth"++ "xpos" "16"++ "ypos" "432"++ "wide" "102"++ "tall" "36"++ "visible" "1"++ "enabled" "1"++++ "PaintBackgroundType" "2"++++ "text_xpos" "8"++ "text_ypos" "20"++ "digit_xpos" "50"++ "digit_ypos" "2"++ }++ HudHealth++ {++ "fieldName" "HudHealth"++ "xpos" "16"++ "ypos" "426"++ "wide" "130"++ "tall" "42"++ "visible" "1"++ "enabled" "1"++++ "PaintBackgroundType" "2"++++ "text_xpos" "8"++ "text_ypos" "23"++ "digit_xpos" "66"++ "digit_ypos" "0"++ }++++ TargetID++ {++ "fieldName" "TargetID"++ "visible" "1"++ "enabled" "1"++ "wide" "640"++ "tall" "480"++ }++++ TeamDisplay++ {++ "fieldName" "TeamDisplay"++ "visible" "0"++ "enabled" "1"++ "xpos" "16"++ "ypos" "410"++ "ypos" "400"++ "wide" "200"++ "tall" "60"++ "text_xpos" "8"++ "text_ypos" "4"++ }++++ HudVoiceSelfStatus++ {++ "fieldName" "HudVoiceSelfStatus"++ "visible" "1"++ "enabled" "1"++ "xpos" "r43"++ "ypos" "355"++ "wide" "24"++ "tall" "24"++ }++++ HudVoiceStatus++ {++ "fieldName" "HudVoiceStatus"++ "visible" "1"++ "enabled" "1"++ "xpos" "r145"++ "ypos" "0"++ "wide" "145"++ "tall" "400"++++ "item_wide" "135"++++ "show_avatar" "0"++++ "show_dead_icon" "1"++ "dead_xpos" "1"++ "dead_ypos" "0"++ "dead_wide" "16"++ "dead_tall" "16"++++ "show_voice_icon" "1"++ "icon_ypos" "0"++ "icon_xpos" "15"++ "icon_tall" "16"++ "icon_wide" "16"++++ "text_xpos" "33"++ }++++ HudSuit++ {++ "fieldName" "HudSuit"++ "xpos" "140"++ "ypos" "432"++ "wide" "108"++ "tall" "36"++ "visible" "1"++ "enabled" "1"++++ "PaintBackgroundType" "2"++++ "text_xpos" "8"++ "text_ypos" "20"++ "digit_xpos" "50"++ "digit_ypos" "2"++ }++ HudSuit++ {++ "fieldName" "HudSuit"++ "xpos" "150"++ "ypos" "426"++ "wide" "120"++ "tall" "42"++ "visible" "1"++ "enabled" "1"++++ "PaintBackgroundType" "2"++++ "text_xpos" "8"++ "text_ypos" "23"++ "digit_xpos" "56"++ "digit_ypos" "0"++ }++++++ HudAmmo++ {++ "fieldName" "HudAmmo"++ "xpos" "r150"++ "ypos" "432"++ "wide" "136"++ "tall" "36"++ "visible" "1"++ "enabled" "1"++++ "PaintBackgroundType" "2"++++ "text_xpos" "8"++ "text_ypos" "20"++ "digit_xpos" "44"++ "digit_ypos" "2"++ "digit2_xpos" "98"++ "digit2_ypos" "16"++ }++ HudAmmo++ {++ "fieldName" "HudAmmo"++ "xpos" "r150"++ "ypos" "426"++ "wide" "152"++ "tall" "42"++ "visible" "1"++ "enabled" "1"++++ "PaintBackgroundType" "2"++++ "text_xpos" "8"++ "text_ypos" "24"++ "digit_xpos" "55"++ "digit_ypos" "0"++ "digit2_xpos" "100"++ "digit2_ypos" "12"++ }++++ HudAmmoSecondary++ {++ "fieldName" "HudAmmoSecondary"++ "xpos" "r76"++ "ypos" "432"++ "wide" "60"++ "tall" "36"++ "visible" "1"++ "enabled" "1"++++ "PaintBackgroundType" "2"++++ "text_xpos" "8"++ "text_ypos" "22"++ "digit_xpos" "36"++ "digit_ypos" "2"++ }++ HudAmmoSecondary++ {++ "fieldName" "HudAmmoSecondary"++ "xpos" "r82"++ "ypos" "426"++ "wide" "70"++ "tall" "42"++ "visible" "1"++ "enabled" "1"++++ "PaintBackgroundType" "2"++++ "text_xpos" "8"++ "text_ypos" "24"++ "digit_xpos" "42"++ "digit_ypos" "0"++ }++++ HudSuitPower++ {++ "fieldName" "HudSuitPower"++ "visible" "1"++ "enabled" "1"++ "xpos" "16"++ "ypos" "396"++ "wide" "102"++ "tall" "26"++++ "AuxPowerLowColor" "255 0 0 220"++ "AuxPowerHighColor" "255 220 0 220"++ "AuxPowerDisabledAlpha" "70"++++ "BarInsetX" "8"++ "BarInsetY" "15"++ "BarWidth" "92"++ "BarHeight" "4"++ "BarChunkWidth" "6"++ "BarChunkGap" "3"++++ "text_xpos" "8"++ "text_ypos" "4"++ "text2_xpos" "8"++ "text2_ypos" "22"++ "text2_gap" "10"++++ "PaintBackgroundType" "2"++ }++++ HudSuitPower++ {++ "fieldName" "HudSuitPower"++ "visible" "1"++ "enabled" "1"++ "xpos" "16"++ "ypos" "386"++ "wide" "112"++ "tall" "54"++++ "AuxPowerLowColor" "255 0 0 220"++ "AuxPowerHighColor" "255 220 0 220"++ "AuxPowerDisabledAlpha" "70"++++ "BarInsetX" "8"++ "BarInsetY" "18"++ "BarWidth" "102"++ "BarHeight" "5"++ "BarChunkWidth" "6"++ "BarChunkGap" "3"++++ "text_xpos" "8"++ "text_ypos" "4"++ "text2_xpos" "8"++ "text2_ypos" "26"++ "text2_gap" "14"++++ "PaintBackgroundType" "2"++ }++++ HudPosture++ {++ "fieldName" "HudPosture"++ "visible" "1"++ "PaintBackgroundType" "2"++ "xpos" "16"++ "ypos" "316"++ "tall" "35"++ "wide" "36"++ "font" "WeaponIconsSmall"++ "icon_xpos" "8"++ "icon_ypos" "-2"++ }++ HudPosture++ {++ "fieldName" "HudPosture"++ "visible" "1"++ "PaintBackgroundType" "2"++ "xpos" "48"++ "ypos" "316"++ "tall" "36"++ "wide" "36"++ "font" "WeaponIconsSmall"++ "icon_xpos" "10"++ "icon_ypos" "2"++ }++++ HudFlashlight++ {++ "fieldName" "HudFlashlight"++ "visible" "1"++ "PaintBackgroundType" "2"++ "xpos" "270"++ "ypos" "444"++ "ypos" "436"++ "xpos_hidef" "306" // aligned to left++ "xpos_lodef" "c-18" // centered in screen++ "ypos" "428"++ "tall" "24"++ "tall" "30"++ "wide" "36"++ "wide" "46"++ "font" "WeaponIconsSmall"++ "font" "FlashlightDeck"++++ "icon_xpos" "4"++ "icon_ypos" "-8"++ "icon_ypos" "-12"++++ "BarInsetX" "4"++ "BarInsetY" "18"++ "BarInsetY" "22"++ "BarWidth" "28"++ "BarWidth" "36"++ "BarHeight" "2"++ "BarChunkWidth" "2"++ "BarHeight" "3"++ "BarChunkWidth" "3"++ "BarChunkGap" "1"++ }++ HudDamageIndicator++ {++ "fieldName" "HudDamageIndicator"++ "visible" "1"++ "enabled" "1"++ "DmgColorLeft" "255 0 0 0"++ "DmgColorRight" "255 0 0 0"++++ "dmg_xpos" "30"++ "dmg_ypos" "100"++ "dmg_wide" "36"++ "dmg_tall1" "240"++ "dmg_tall2" "200"++ }++++ HudZoom++ {++ "fieldName" "HudZoom"++ "visible" "1"++ "enabled" "1"++ "Circle1Radius" "66"++ "Circle2Radius" "74"++ "DashGap" "16"++ "DashHeight" "4"++ "DashHeight" "6"++ "BorderThickness" "88"++ }++ HudWeaponSelection++ {++ "fieldName" "HudWeaponSelection"++ "ypos" "16"++ "ypos" "32"++ "visible" "1"++ "enabled" "1"++ "SmallBoxSize" "32"++ "MediumBoxWide" "95"++ "MediumBoxWide_hidef" "78"++ "MediumBoxTall" "50"++ "MediumBoxTall_hidef" "50"++ "MediumBoxWide_lodef" "74"++ "MediumBoxTall_lodef" "50"++ "LargeBoxWide" "112"++ "LargeBoxTall" "84"++ "BoxGap" "8"++ "SelectionNumberXPos" "4"++ "SelectionNumberYPos" "4"++ "SelectionGrowTime" "0.4"++ "TextYPos" "64"++ }++++ HudCrosshair++ {++ "fieldName" "HudCrosshair"++ "visible" "1"++ "enabled" "1"++ "wide" "640"++ "tall" "480"++ }++++ HudDeathNotice++ {++ "fieldName" "HudDeathNotice"++ "visible" "1"++ "enabled" "1"++ "wide" "640"++ "tall" "480"++ }++++ HudVehicle++ {++ "fieldName" "HudVehicle"++ "visible" "1"++ "enabled" "1"++ "wide" "640"++ "tall" "480"++ }++++ ScorePanel++ {++ "fieldName" "ScorePanel"++ "visible" "1"++ "enabled" "1"++ "wide" "640"++ "tall" "480"++ }++++ HudTrain++ {++ "fieldName" "HudTrain"++ "visible" "1"++ "enabled" "1"++ "wide" "640"++ "tall" "480"++ }++++ HudMOTD++ {++ "fieldName" "HudMOTD"++ "visible" "1"++ "enabled" "1"++ "wide" "640"++ "tall" "480"++ }++++ HudMessage++ {++ "fieldName" "HudMessage"++ "visible" "1"++ "enabled" "1"++ "wide" "f0"++ "tall" "480"++ }++++ HudMenu++ {++ "fieldName" "HudMenu"++ "visible" "1"++ "enabled" "1"++ "wide" "640"++ "tall" "480"++ }++++ HudCloseCaption++ {++ "fieldName" "HudCloseCaption"++ "visible" "1"++ "enabled" "1"++ "xpos" "c-250"++ "ypos" "276"++ "ypos" "236"++ "wide" "500"++ "tall" "136"++ "tall" "176"++++ "BgAlpha" "128"++++ "GrowTime" "0.25"++ "ItemHiddenTime" "0.2" // Nearly same as grow time so that the item doesn't start to show until growth is finished++ "ItemFadeInTime" "0.15" // Once ItemHiddenTime is finished, takes this much longer to fade in++ "ItemFadeOutTime" "0.3"++ "topoffset" "0"++ "topoffset" "0"++ }++++ HudChat++ {++ "fieldName" "HudChat"++ "visible" "0"++ "enabled" "1"++ "xpos" "0"++ "ypos" "0"++ "wide" "4"++ "tall" "4"++ }++++ HudHistoryResource++ {++ "fieldName" "HudHistoryResource"++ "visible" "1"++ "enabled" "1"++ "xpos" "r272"++ "xpos" "r252"++ "ypos" "40"++ "wide" "248"++ "tall" "320"++++ "history_gap" "64"++ "history_gap" "56"++ "icon_inset" "38"++ "text_inset" "36"++ "text_inset" "26"++ "NumberFont" "HudNumbersSmall"++ }++++ HudGeiger++ {++ "fieldName" "HudGeiger"++ "visible" "1"++ "enabled" "1"++ "wide" "640"++ "tall" "480"++ }++++ HUDQuickInfo++ {++ "fieldName" "HUDQuickInfo"++ "visible" "1"++ "enabled" "1"++ "wide" "640"++ "tall" "480"++ }++++ HudWeapon++ {++ "fieldName" "HudWeapon"++ "visible" "1"++ "enabled" "1"++ "wide" "640"++ "tall" "480"++ }++ HudAnimationInfo++ {++ "fieldName" "HudAnimationInfo"++ "visible" "1"++ "enabled" "1"++ "wide" "640"++ "tall" "480"++ }++++ HudPredictionDump++ {++ "fieldName" "HudPredictionDump"++ "visible" "1"++ "enabled" "1"++ "wide" "3840"++ "tall" "1080"++ }++++ HudHintDisplay++ {++ "fieldName" "HudHintDisplay"++ "visible" "0"++ "enabled" "1"++ "xpos" "c-240"++ "ypos" "c60"++ "xpos" "r148"++ "ypos" "r338"++ "wide" "480"++ "tall" "100"++ "HintSize" "1"++ "text_xpos" "8"++ "text_ypos" "8"++ "center_x" "0" // center text horizontally++ "center_y" "-1" // align text on the bottom++ "paintbackground" "0"++ }++++ HudHintKeyDisplay++ {++ "fieldName" "HudHintKeyDisplay"++ "visible" "0"++ "enabled" "1"++ "xpos" "r120"++ "ypos" "r340"++ "xpos" "r148"++ "ypos" "r338"++ "wide" "100"++ "tall" "200"++ "text_xpos" "8"++ "text_ypos" "8"++ "text_xgap" "8"++ "text_ygap" "8"++ "TextColor" "255 170 0 220"++++ "PaintBackgroundType" "2"++ }++++++ HudSquadStatus++ {++ "fieldName" "HudSquadStatus"++ "visible" "1"++ "enabled" "1"++ "xpos" "r120"++ "ypos" "380"++ "wide" "104"++ "tall" "46"++ "text_xpos" "8"++ "text_ypos" "34"++ "SquadIconColor" "255 220 0 160"++ "IconInsetX" "8"++ "IconInsetY" "0"++ "IconGap" "24"++++ "PaintBackgroundType" "2"++ }++ HudSquadStatus++ {++ "fieldName" "HudSquadStatus"++ "visible" "1"++ "enabled" "1"++ "xpos" "r160"++ "ypos" "372"++ "wide" "144"++ "tall" "46"++ "text_xpos" "8"++ "text_ypos" "28"++ "SquadIconColor" "255 220 0 160"++ "IconInsetX" "8"++ "IconInsetY" "-10"++ "IconGap" "39"++++ "PaintBackgroundType" "2"++ }++++++ HudPoisonDamageIndicator++ {++ "fieldName" "HudPoisonDamageIndicator"++ "visible" "0"++ "enabled" "1"++ "xpos" "16"++ "ypos" "338"++ "wide" "136"++ "tall" "38"++ "text_xpos" "8"++ "text_ypos" "8"++ "text_ygap" "14"++ "TextColor" "255 170 0 220"++ "PaintBackgroundType" "2"++ }++ HudPoisonDamageIndicator++ {++ "fieldName" "HudPoisonDamageIndicator"++ "visible" "0"++ "enabled" "1"++ "xpos" "16"++ "ypos" "262"++ "wide" "190"++ "tall" "42"++ "text_xpos" "8"++ "text_ypos" "8"++ "text_ygap" "14"++ "TextColor" "255 170 0 220"++ "PaintBackgroundType" "2"++ }++ HudCredits++ {++ "fieldName" "HudCredits"++ "TextFont" "Default"++ "visible" "1"++ "xpos" "0"++ "ypos" "0"++ "wide" "640"++ "tall" "480"++ "TextColor" "255 255 255 192"++++ }++++ HUDAutoAim++ {++ "fieldName" "HUDAutoAim"++ "visible" "1"++ "enabled" "1"++ "wide" "640"++ "tall" "480"++ "wide" "960"++ "tall" "720"++ }++++ HudCommentary++ {++ "fieldName" "HudCommentary"++ "xpos" "c-190"++ "ypos" "350"++ "wide" "380"++ "tall" "40"++ "visible" "1"++ "enabled" "1"++++ "PaintBackgroundType" "2"++++ "bar_xpos" "50"++ "bar_ypos" "20"++ "bar_height" "8"++ "bar_width" "320"++ "speaker_xpos" "50"++ "speaker_ypos" "8"++ "count_xpos_from_right" "10" // Counts from the right side++ "count_ypos" "8"++++ "icon_texture" "vgui/hud/icon_commentary"++ "icon_xpos" "0"++ "icon_ypos" "0"++ "icon_width" "40"++ "icon_height" "40"++ }++++ HudHDRDemo++ {++ "fieldName" "HudHDRDemo"++ "xpos" "0"++ "ypos" "0"++ "wide" "640"++ "tall" "480"++ "visible" "1"++ "enabled" "1"++++ "Alpha" "255"++ "PaintBackgroundType" "2"++++ "BorderColor" "0 0 0 255"++ "BorderLeft" "16"++ "BorderRight" "16"++ "BorderTop" "16"++ "BorderBottom" "64"++ "BorderCenter" "0"++++ "TextColor" "255 255 255 255"++ "LeftTitleY" "422"++ "RightTitleY" "422"++ }++++ AchievementNotificationPanel++ {++ "fieldName" "AchievementNotificationPanel"++ "visible" "1"++ "enabled" "1"++ "xpos" "0"++ "ypos" "180"++ "wide" "f10"++ "wide" "f60"++ "tall" "100"++ }++++ CHudVote++ {++ "fieldName" "CHudVote"++ "xpos" "0"++ "ypos" "0"++ "wide" "640"++ "tall" "480"++ "visible" "1"++ "enabled" "1"++ "bgcolor_override" "0 0 0 0"++ "PaintBackgroundType" "0" // rounded corners++ }++}\\ No newline at end of file@@@ -1,4 +1,3 @@--soundscaples_manifest++"scripts/soundscapes_manifest.txt"{-- "file" "scripts/soundscapes_openvibe.txt"}nnew file mode 100644@@@ -0,0 +1 @@++// OpenVibe: Source intentionally leaves the generic mapspawn hook empty.nnew file mode 100644@@@ -0,0 +1,68 @@++// 357++++WeaponData++{++ // Weapon data is loaded by both the Game and Client DLLs.++ "printname" "#HL2_357Handgun"++ "viewmodel" "models/weapons/v_357.mdl"++ "playermodel" "models/weapons/w_357.mdl"++ "anim_prefix" "python"++ "bucket" "1"++ "bucket_position" "1"++ "bucket_360" "0"++ "bucket_position_360" "1"++++ "clip_size" "6"++ "default_clip" "6"++ "primary_ammo" "357"++ "secondary_ammo" "None"++++ "weight" "7"++ "rumble" "2"++ "item_flags" "0"++++ // Sounds for the weapon. There is a max of 16 sounds per category (i.e. max 16 "single_shot" sounds)++ SoundData++ {++ "empty" "Weapon_Pistol.Empty"++ "single_shot" "Weapon_357.Single"++ }++++ // Weapon Sprite data is loaded by the Client DLL.++ TextureData++ {++ "weapon"++ {++ "font" "WeaponIcons"++ "character" "e"++ }++ "weapon_s"++ {++ "font" "WeaponIconsSelected"++ "character" "e"++ }++ "weapon_small"++ {++ "font" "WeaponIconsSmall"++ "character" "e"++ }++ "ammo"++ {++ "font" "WeaponIconsSmall"++ "character" "q"++ }++ "crosshair"++ {++ "font" "Crosshairs"++ "character" "Q"++ }++ "autoaim"++ {++ "file" "sprites/crosshairs"++ "x" "0"++ "y" "48"++ "width" "24"++ "height" "24"++ }++ }++}\\ No newline at end of filennew file mode 100644@@@ -0,0 +1,85 @@++// Assault Rifle 2++++WeaponData++{++ // Weapon data is loaded by both the Game and Client DLLs.++ "printname" "#HL2_Pulse_Rifle"++ "viewmodel" "models/weapons/v_irifle.mdl"++ "playermodel" "models/weapons/w_irifle.mdl"++ "anim_prefix" "ar2"++ "bucket" "2"++ "bucket_position" "1"++ "bucket_360" "1"++ "bucket_position_360" "2"++++ "clip_size" "30"++ "default_clip" "30"++++ "clip2_size" "-1"++ "default_clip2" "-1"++++ "primary_ammo" "AR2"++ "secondary_ammo" "AR2AltFire"++++ "weight" "5"++ "rumble" "4"++ "item_flags" "0"++++ // Sounds for the weapon. There is a max of 16 sounds per category (i.e. max 16 "single_shot" sounds)++ SoundData++ {++ "special1" "Weapon_CombineGuard.Special1"++ "empty" "Weapon_IRifle.Empty"++ "double_shot" "Weapon_IRifle.Single"++ "reload" "Weapon_AR2.Reload"++ "single_shot" "Weapon_AR2.Single"++++ // NPC SECTION++ "single_shot_npc" "Weapon_AR2.NPC_Single"++ "reload_npc" "Weapon_AR2.NPC_Reload"++ "double_shot_npc" "Weapon_AR2.NPC_Double"++ }++++ // Weapon Sprite data is loaded by the Client DLL.++ TextureData++ {++ "weapon"++ {++ "font" "WeaponIcons"++ "character" "l"++ }++ "weapon_s"++ {++ "font" "WeaponIconsSelected"++ "character" "l"++ }++ "weapon_small"++ {++ "font" "WeaponIconsSmall"++ "character" "l"++ }++ "ammo"++ {++ "font" "WeaponIconsSmall"++ "character" "u"++ }++ "ammo2"++ {++ "font" "WeaponIconsSmall"++ "character" "z"++ }++ "crosshair"++ {++ "font" "Crosshairs"++ "character" "Q"++ }++ "autoaim"++ {++ "file" "sprites/crosshairs"++ "x" "0"++ "y" "48"++ "width" "24"++ "height" "24"++ }++ }++}\\ No newline at end of filennew file mode 100644@@@ -0,0 +1,82 @@++// Crossbow++++WeaponData++{++ // Weapon data is loaded by both the Game and Client DLLs.++ "printname" "#HL2_Crossbow"++ "viewmodel" "models/weapons/v_crossbow.mdl"++ "playermodel" "models/weapons/w_crossbow.mdl"++ "anim_prefix" "bow"++ "bucket" "3"++ "bucket_position" "1"++ "bucket_360" "0"++ "bucket_position_360" "2"++++ "clip_size" "1"++ "default_clip" "5"++ "primary_ammo" "XBowBolt"++ "secondary_ammo" "None"++++ "weight" "6"++ "item_flags" "0"++++ // Sounds for the weapon. There is a max of 16 sounds per category (i.e. max 16 "single_shot" sounds)++ SoundData++ {++ "single_shot" "Weapon_Crossbow.Single"++ "reload" "Weapon_Crossbow.Reload"++ "special1" "Weapon_Crossbow.BoltElectrify"++ "special2" "Weapon_Crossbow.BoltFly"++ }++++ // Weapon Sprite data is loaded by the Client DLL.++ TextureData++ {++ "weapon"++ {++ "font" "WeaponIcons"++ "character" "g"++ }++ "weapon_s"++ {++ "font" "WeaponIconsSelected"++ "character" "g"++ }++ "weapon_small"++ {++ "font" "WeaponIconsSmall"++ "character" "g"++ }++ "ammo"++ {++ "font" "WeaponIconsSmall"++ "character" "w"++ }++ "crosshair"++ {++ "font" "Crosshairs"++ "character" "Q"++ }++ "autoaim"++ {++ "file" "sprites/crosshairs"++ "x" "0"++ "y" "96"++ "width" "24"++ "height" "24"++ }++ "zoom"++ {++ "font" "Crosshairs"++ "character" "Q"++ }++ "zoom_autoaim"++ {++ "file" "sprites/crosshairs"++ "x" "24"++ "y" "112"++ "width" "104"++ "height" "16"++ }++ }++}\\ No newline at end of filennew file mode 100644@@@ -0,0 +1,62 @@++// Crowbar++++WeaponData++{++ // Weapon data is loaded by both the Game and Client DLLs.++ "printname" "#HL2_Crowbar"++ "viewmodel" "models/weapons/v_crowbar.mdl"++ "playermodel" "models/weapons/w_crowbar.mdl"++ "anim_prefix" "crowbar"++ "bucket" "0"++ "bucket_position" "0"++ "bucket_360" "2"++ "bucket_position_360" "0"++++ "clip_size" "-1"++ "primary_ammo" "None"++ "secondary_ammo" "None"++++ "weight" "0"++ "item_flags" "0"++++ // Sounds for the weapon. There is a max of 16 sounds per category (i.e. max 16 "single_shot" sounds)++ SoundData++ {++ "single_shot" "Weapon_Crowbar.Single"++ "melee_hit" "Weapon_Crowbar.Melee_Hit"++ "melee_hit_world" "Weapon_Crowbar.Melee_HitWorld"++ }++++ // Weapon Sprite data is loaded by the Client DLL.++ TextureData++ {++ "weapon"++ {++ "font" "WeaponIcons"++ "character" "c"++ }++ "weapon_s"++ {++ "font" "WeaponIconsSelected"++ "character" "c"++ }++ "ammo"++ {++ "font" "WeaponIcons"++ "character" "c"++ }++ "crosshair"++ {++ "font" "Crosshairs"++ "character" "Q"++ }++ "autoaim"++ {++ "file" "sprites/crosshairs"++ "x" "0"++ "y" "48"++ "width" "24"++ "height" "24"++ }++ }++}\\ No newline at end of filennew file mode 100644@@@ -0,0 +1,69 @@++WeaponData++{++ // Weapon data is loaded by both the Game and Client DLLs.++ "printname" "#HL2_Grenade"++ "viewmodel" "models/weapons/v_grenade.mdl"++ "playermodel" "models/items/grenadeAmmo.mdl"++ "anim_prefix" "Grenade"++ "bucket" "4"++ "bucket_position" "0"++ "bucket_360" "3"++ "bucket_position_360" "0"++++ "clip_size" "-1"++ "clip2_size" "-1"++ "default_clip" "1"++ "default_clip2" "-1"++++ "primary_ammo" "grenade"++ "secondary_ammo" "None"++++ "weight" "1"++ "item_flags" "18" // ITEM_FLAG_NOAUTORELOAD | ITEM_FLAG_EXHAUSTIBLE++++ // Sounds for the weapon. There is a max of 16 sounds per category (i.e. max 16 "single_shot" sounds)++ SoundData++ {++ "special1" "WeaponFrag.Roll"++ "double_shot" "common/null.wav"++ "single_shot" "common/null.wav"++ }++++ // Weapon Sprite data is loaded by the Client DLL.++ TextureData++ {++ "weapon"++ {++ "font" "WeaponIcons"++ "character" "k"++ }++ "weapon_s"++ {++ "font" "WeaponIconsSelected"++ "character" "k"++ }++ "weapon_small"++ {++ "font" "WeaponIconsSmall"++ "character" "k"++ }++ "ammo"++ {++ "font" "WeaponIconsSmall"++ "character" "v"++ }++ "crosshair"++ {++ "font" "Crosshairs"++ "character" "Q"++ }++ "autoaim"++ {++ "file" "sprites/crosshairs"++ "x" "48"++ "y" "72"++ "width" "24"++ "height" "24"++ }++ }++}\\ No newline at end of filennew file mode 100644@@@ -0,0 +1,21 @@++weapon_manifest++{++ "file" "scripts/weapon_357.txt"++ "file" "scripts/weapon_alyxgun.txt"++ "file" "scripts/weapon_annabelle.txt"++ "file" "scripts/weapon_ar2.txt"++ "file" "scripts/weapon_bugbait.txt"++ "file" "scripts/weapon_citizenpackage.txt"++ "file" "scripts/weapon_citizensuitcase.txt"++ "file" "scripts/weapon_crossbow.txt"++ "file" "scripts/weapon_crowbar.txt"++ "file" "scripts/weapon_cubemap.txt"++ "file" "scripts/weapon_frag.txt"++ "file" "scripts/weapon_physcannon.txt"++ "file" "scripts/weapon_physgun.txt"++ "file" "scripts/weapon_pistol.txt"++ "file" "scripts/weapon_rpg.txt"++ "file" "scripts/weapon_shotgun.txt"++ "file" "scripts/weapon_smg1.txt"++ "file" "scripts/weapon_stunstick.txt"++}\\ No newline at end of filennew file mode 100644@@@ -0,0 +1,72 @@++// Physics Gun++++WeaponData++{++ // Weapon data is loaded by both the Game and Client DLLs.++ "printname" "#HL2_GravityGun"++ "viewmodel" "models/weapons/v_physcannon.mdl"++ "playermodel" "models/weapons/w_Physics.mdl"++ "anim_prefix" "gauss"++ "bucket" "0"++ "bucket_position" "1"++ "bucket_360" "2"++ "bucket_position_360" "1"++ "clip_size" "1"++ "clip2_size" "0"++ "primary_ammo" "None"++ "secondary_ammo" "None"++ "default_clip" "0"++ "default_clip2" "0"++ "weight" "0"++ "autoswitchto" "0"++ "autoswitchfrom" "0"++ "item_flags" "2"++++ SoundData++ {++ // launch held object++ "single_shot" "Weapon_PhysCannon.Launch"++ // Ignore?++ "reload" "Weapon_PhysCannon.Charge"++ // dry fire - no target++ "empty" "Weapon_PhysCannon.DryFire"++ // pick up object++ "special1" "Weapon_PhysCannon.Pickup"++ // open claws, target object++ "special2" "Weapon_PhysCannon.OpenClaws"++ // close claws, target out of range/invalid++ "melee_hit" "Weapon_PhysCannon.CloseClaws"++ // drop object++ "melee_miss" "Weapon_PhysCannon.Drop"++ // too heavy++ "special3" "Weapon_PhysCannon.TooHeavy"++ }++++ // Weapon Sprite data is loaded by the Client DLL.++ TextureData++ {++ "weapon"++ {++ "font" "WeaponIcons"++ "character" "m"++ }++ "weapon_s"++ {++ "font" "WeaponIconsSelected"++ "character" "m"++ }++ "crosshair"++ {++ "font" "Crosshairs"++ "character" "Q"++ }++ "autoaim"++ {++ "file" "sprites/crosshairs"++ "x" "48"++ "y" "72"++ "width" "24"++ "height" "24"++ }++ }++}\\ No newline at end of filennew file mode 100644@@@ -0,0 +1,74 @@++// Pistol++++WeaponData++{++ // Weapon data is loaded by both the Game and Client DLLs.++ "printname" "#HL2_Pistol"++ "viewmodel" "models/weapons/v_pistol.mdl"++ "playermodel" "models/weapons/w_pistol.mdl"++ "anim_prefix" "pistol"++ "bucket" "1"++ "bucket_position" "0"++ "bucket_360" "0"++ "bucket_position_360" "0"++++ "clip_size" "18"++ "primary_ammo" "Pistol"++ "secondary_ammo" "None"++++ "weight" "2"++ "rumble" "1"++ "item_flags" "0"++++ // Sounds for the weapon. There is a max of 16 sounds per category (i.e. max 16 "single_shot" sounds)++ SoundData++ {++++ "reload" "Weapon_Pistol.Reload"++ "reload_npc" "Weapon_Pistol.NPC_Reload"++ "empty" "Weapon_Pistol.Empty"++ "single_shot" "Weapon_Pistol.Single"++ "single_shot_npc" "Weapon_Pistol.NPC_Single"++ "special1" "Weapon_Pistol.Special1"++ "special2" "Weapon_Pistol.Special2"++ "burst" "Weapon_Pistol.Burst"++ }++++ // Weapon Sprite data is loaded by the Client DLL.++ TextureData++ {++ "weapon"++ {++ "font" "WeaponIcons"++ "character" "d"++ }++ "weapon_s"++ {++ "font" "WeaponIconsSelected"++ "character" "d"++ }++ "weapon_small"++ {++ "font" "WeaponIconsSmall"++ "character" "d"++ }++ "ammo"++ {++ "font" "WeaponIconsSmall"++ "character" "p"++ }++ "crosshair"++ {++ "font" "Crosshairs"++ "character" "Q"++ }++ "autoaim"++ {++ "file" "sprites/crosshairs"++ "x" "0"++ "y" "48"++ "width" "24"++ "height" "24"++ }++ }++}\\ No newline at end of filennew file mode 100644@@@ -0,0 +1,77 @@++// Missile Launcher++++WeaponData++{++ // Weapon data is loaded by both the Game and Client DLLs.++ "printname" "#HL2_RPG"++ "viewmodel" "models/weapons/v_rpg.mdl"++ "playermodel" "models/weapons/w_rocket_launcher.mdl"++ "anim_prefix" "missile launcher"++ "bucket" "4"++ "bucket_position" "1"++ "autoswitchfrom" "0"++ "bucket_360" "3"++ "bucket_position_360" "1"++++ "clip_size" "-1"++ "clip2_size" "-1"++ "default_clip" "3"++ "default_clip2" "-1"++++ "primary_ammo" "rpg_round"++ "secondary_ammo" "None"++++ "autoswitchto" "0"++ "weight" "0"++ "item_flags" "2"++++ // Sounds for the weapon. There is a max of 16 sounds per category (i.e. max 16 "single_shot" sounds)++ SoundData++ {++ "single_shot" "Weapon_RPG.Single"++ "single_shot_npc" "Weapon_RPG.NPC_Single"++ //Laser on++ "special1" "Weapon_RPG.LaserOn"++ //Laser off++ "special2" "Weapon_RPG.LaserOff"++ "empty" "Weapon_SMG1.Empty"++ }++++ // Weapon Sprite data is loaded by the Client DLL.++ TextureData++ {++ "weapon"++ {++ "font" "WeaponIcons"++ "character" "i"++ }++ "weapon_s"++ {++ "font" "WeaponIconsSelected"++ "character" "i"++ }++ "weapon_small"++ {++ "font" "WeaponIconsSmall"++ "character" "i"++ }++ "ammo"++ {++ "font" "WeaponIconsSmall"++ "character" "x"++ }++ "crosshair"++ {++ "font" "Crosshairs"++ "character" "Q"++ }++ "autoaim"++ {++ "file" "sprites/crosshairs"++ "x" "48"++ "y" "72"++ "width" "24"++ "height" "24"++ }++ }++}\\ No newline at end of filennew file mode 100644@@@ -0,0 +1,74 @@++// Shotgun++++WeaponData++{++ // Weapon data is loaded by both the Game and Client DLLs.++ "printname" "#HL2_Shotgun"++ "viewmodel" "models/weapons/v_shotgun.mdl"++ "playermodel" "models/weapons/w_shotgun.mdl"++ "anim_prefix" "shotgun"++ "bucket" "3"++ "bucket_position" "0"++ "bucket_360" "1"++ "bucket_position_360" "1"++++ "clip_size" "6"++ "primary_ammo" "Buckshot"++ "secondary_ammo" "None"++++ "weight" "4"++ "rumble" "5"++ "item_flags" "0"++++ // Sounds for the weapon. There is a max of 16 sounds per category (i.e. max 16 "single_shot" sounds)++ SoundData++ {++++ "empty" "Weapon_Shotgun.Empty"++ "reload" "Weapon_Shotgun.Reload"++ "special1" "Weapon_Shotgun.Special1"++ "single_shot" "Weapon_Shotgun.Single"++ "double_shot" "Weapon_Shotgun.Double"++ // NPC WEAPON SOUNDS++ "reload_npc" "Weapon_Shotgun.NPC_Reload"++ "single_shot_npc" "Weapon_Shotgun.NPC_Single"++ }++++ // Weapon Sprite data is loaded by the Client DLL.++ TextureData++ {++ "weapon"++ {++ "font" "WeaponIcons"++ "character" "b"++ }++ "weapon_s"++ {++ "font" "WeaponIconsSelected"++ "character" "b"++ }++ "weapon_small"++ {++ "font" "WeaponIconsSmall"++ "character" "b"++ }++ "ammo"++ {++ "font" "WeaponIconsSmall"++ "character" "s"++ }++ "crosshair"++ {++ "font" "Crosshairs"++ "character" "Q"++ }++ "autoaim"++ {++ "file" "sprites/crosshairs"++ "x" "0"++ "y" "48"++ "width" "24"++ "height" "24"++ }++ }++}\\ No newline at end of filennew file mode 100644@@@ -0,0 +1,25 @@++WeaponData++{++ "printname" "#HL2_SLAM"++ "viewmodel" "models/weapons/v_slam.mdl"++ "playermodel" "models/weapons/w_slam.mdl"++ "anim_prefix" "slam"++ "bucket" "4"++ "bucket_position" "1"++++ "clip_size" "-1"++ "clip2_size" "-1"++ "default_clip" "1"++ "default_clip2" "-1"++++ "primary_ammo" "slam"++ "secondary_ammo" "None"++ "weight" "5"++ "item_flags" "18"++++ SoundData++ {++ "single_shot" "Weapon_SLAM.SatchelThrow"++ "special1" "Weapon_SLAM.SatchelDetonate"++ }++}nnew file mode 100644@@@ -0,0 +1,84 @@++// Small Machine Gun 1++++WeaponData++{++ // Weapon data is loaded by both the Game and Client DLLs.++ "printname" "#HL2_SMG1"++ "viewmodel" "models/weapons/v_smg1.mdl"++ "playermodel" "models/weapons/w_smg1.mdl" //FIXME:++ "anim_prefix" "smg2"++ "bucket" "2"++ "bucket_position" "0"++ "bucket_360" "1"++ "bucket_position_360" "0"++++ "clip_size" "45"++ "clip2_size" "-1"++++ "default_clip" "45"++ "default_clip2" "-1"++++ "primary_ammo" "SMG1"++ "secondary_ammo" "SMG1_Grenade"++++ "weight" "3"++ "rumble" "3"++ "item_flags" "0"++++ // Sounds for the weapon. There is a max of 16 sounds per category (i.e. max 16 "single_shot" sounds)++ SoundData++ {++ "reload" "Weapon_SMG1.Reload"++ "reload_npc" "Weapon_SMG1.NPC_Reload"++ "empty" "Weapon_SMG1.Empty"++ "single_shot" "Weapon_SMG1.Single"++ "single_shot_npc" "Weapon_SMG1.NPC_Single"++ "special1" "Weapon_SMG1.Special1"++ "special2" "Weapon_SMG1.Special2"++ "double_shot" "Weapon_SMG1.Double"++ "burst" "Weapon_SMG1.Burst"++ }++++ // Weapon Sprite data is loaded by the Client DLL.++ TextureData++ {++ "weapon"++ {++ "font" "WeaponIcons"++ "character" "a"++ }++ "weapon_s"++ {++ "font" "WeaponIconsSelected"++ "character" "a"++ }++ "weapon_small"++ {++ "font" "WeaponIconsSmall"++ "character" "a"++ }++ "ammo"++ {++ "font" "WeaponIconsSmall"++ "character" "r"++ }++ "ammo2"++ {++ "font" "WeaponIconsSmall"++ "character" "t"++ }++ "crosshair"++ {++ "font" "Crosshairs"++ "character" "Q"++ }++ "autoaim"++ {++ "file" "sprites/crosshairs"++ "x" "0"++ "y" "48"++ "width" "24"++ "height" "24"++ }++ }++}\\ No newline at end of filennew file mode 100644@@@ -0,0 +1,65 @@++// stunstick++++WeaponData++{++ // Weapon data is loaded by both the Game and Client DLLs.++ "printname" "#HL2_StunBaton"++ "viewmodel" "models/weapons/v_stunbaton.mdl"++ "playermodel" "models/weapons/w_stunbaton.mdl"++ "anim_prefix" "stunbaton"++ "bucket" "0"++ "bucket_position" "3"++++ "clip_size" "-1"++ "primary_ammo" "None"++ "secondary_ammo" "None"++++ "weight" "0"++ "item_flags" "0"++++ // Sounds for the weapon. There is a max of 16 sounds per category (i.e. max 16 "single_shot" sounds)++ SoundData++ {++++ "single_shot" "Weapon_StunStick.Swing"++ "melee_miss" "Weapon_StunStick.Melee_Miss"++ "melee_hit" "Weapon_StunStick.Melee_Hit"++ "melee_hit_world" "Weapon_StunStick.Melee_HitWorld"++ }++++ // Weapon Sprite data is loaded by the Client DLL.++ TextureData++ {++ "weapon"++ {++ "font" "WeaponIcons"++ "character" "n"++ }++ "weapon_s"++ {++ "font" "WeaponIconsSelected"++ "character" "n"++ }++ "ammo"++ {++ "font" "WeaponIcons"++ "character" "n"++ }++ "crosshair"++ {++ "file" "sprites/crosshairs"++ "x" "0"++ "y" "48"++ "width" "24"++ "height" "24"++ }++ "autoaim"++ {++ "file" "sprites/crosshairs"++ "x" "48"++ "y" "72"++ "width" "24"++ "height" "24"++ }++ }++}\\ No newline at end of file@@@ -865,7 +865,7 @@ world"wait" "1"connections{-- "OnStartTouch" "cmd_return_hub,Command,ov_join hub,0,-1"++ "OnStartTouch" "cmd_return_hub,Command,connect 127.0.0.1:27015,0,-1"}solid{@@@ -865,7 +865,7 @@ world"wait" "1"connections{-- "OnStartTouch" "cmd_return_hub,Command,ov_join hub,0,-1"++ "OnStartTouch" "cmd_return_hub,Command,connect 127.0.0.1:27015,0,-1"}solid{@@@ -1026,7 +1026,6 @@ world"angles" "0 0 0""model" "models/Humans/Group03/male_07.mdl""solid" "0"-- "DefaultAnim" "idle_all_01"}entity{@@@ -1037,7 +1036,6 @@ world"angles" "0 0 0""model" "models/Humans/Group03/male_07.mdl""solid" "0"-- "DefaultAnim" "idle_all_01"}entity{@@@ -1080,7 +1078,7 @@ world"wait" "1"connections{-- "OnStartTouch" "cmd_join_prophunt,Command,ov_join prophunt,0,-1"++ "OnStartTouch" "cmd_join_prophunt,Command,connect 127.0.0.2:27016,0,-1"}solid{@@@ -1178,7 +1176,7 @@ world"wait" "1"connections{-- "OnStartTouch" "cmd_join_deathrun,Command,ov_join deathrun,0,-1"++ "OnStartTouch" "cmd_join_deathrun,Command,connect 127.0.0.3:27017,0,-1"}solid{@@@ -1276,7 +1274,7 @@ world"wait" "1"connections{-- "OnStartTouch" "cmd_join_fortwars,Command,ov_join fortwars,0,-1"++ "OnStartTouch" "cmd_join_fortwars,Command,connect 127.0.0.4:27018,0,-1"}solid{@@@ -1374,7 +1372,7 @@ world"wait" "1"connections{-- "OnStartTouch" "cmd_join_traitortown,Command,ov_join traitortown,0,-1"++ "OnStartTouch" "cmd_join_traitortown,Command,connect 127.0.0.5:27019,0,-1"}solid{@@@ -865,7 +865,7 @@ world"wait" "1"connections{-- "OnStartTouch" "cmd_return_hub,Command,ov_join hub,0,-1"++ "OnStartTouch" "cmd_return_hub,Command,connect 127.0.0.1:27015,0,-1"}solid{@@@ -865,7 +865,7 @@ world"wait" "1"connections{-- "OnStartTouch" "cmd_return_hub,Command,ov_join hub,0,-1"++ "OnStartTouch" "cmd_return_hub,Command,connect 127.0.0.1:27015,0,-1"}solid{@@@ -137,14 +137,12 @@ ipcMain.handle('game:launch', (_e, { ip, port }) => {ipcMain.handle('game:launch-direct', (_e, mode) => {// Look up the mode's port from local server list-- // Use 127.0.1.1 (workstation hostname) instead of 127.0.0.1 since that's where-- // the Source engine binds the server when resolving localhost via /etc/hostsconst serverMap = {-- hub: { ip: '127.0.1.1', port: 27015 },-- prophunt: { ip: '127.0.1.1', port: 27016 },-- deathrun: { ip: '127.0.1.1', port: 27017 },-- fortwars: { ip: '127.0.1.1', port: 27018 },-- traitortown: { ip: '127.0.1.1', port: 27019 },++ hub: { ip: '127.0.0.1', port: 27015 },++ prophunt: { ip: '127.0.0.2', port: 27016 },++ deathrun: { ip: '127.0.0.3', port: 27017 },++ fortwars: { ip: '127.0.0.4', port: 27018 },++ traitortown: { ip: '127.0.0.5', port: 27019 },};const srv = serverMap[mode] || serverMap.hub;return launchGame(srv.ip, srv.port);@@@ -13,7 +13,7 @@"serverSecret": "dev-secret","mode": "prophunt","mapName": "ph_openvibe_dev",-- "publicHost": "127.0.0.1",++ "publicHost": "127.0.0.2","port": 27016,"maxPlayers": 24},@@@ -22,7 +22,7 @@"serverSecret": "dev-secret","mode": "deathrun","mapName": "dr_openvibe_dev",-- "publicHost": "127.0.0.1",++ "publicHost": "127.0.0.3","port": 27017,"maxPlayers": 24},@@@ -31,7 +31,7 @@"serverSecret": "dev-secret","mode": "fortwars","mapName": "fw_openvibe_dev",-- "publicHost": "127.0.0.1",++ "publicHost": "127.0.0.4","port": 27018,"maxPlayers": 32},@@@ -40,7 +40,7 @@"serverSecret": "dev-secret","mode": "traitortown","mapName": "tt_openvibe_dev",-- "publicHost": "127.0.0.1",++ "publicHost": "127.0.0.5","port": 27019,"maxPlayers": 24}nnew file mode 100755@@@ -0,0 +1,20 @@++#!/usr/bin/env bash++set -euo pipefail++++ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"++OUT_DIR="${OPENVIBE_BACKUP_DIR:-$ROOT/backups}"++DATABASE_URL="${DATABASE_URL:-postgres://openvibe:[email protected]:5432/openvibe}"++STAMP="$(date -u +%Y%m%dT%H%M%SZ)"++OUT="$OUT_DIR/openvibe-$STAMP.sql.gz"++++mkdir -p "$OUT_DIR"++++if ! command -v pg_dump >/dev/null 2>&1; then++ echo "pg_dump is required. Install postgresql-client." >&2++ exit 1++fi++++pg_dump "$DATABASE_URL" | gzip -9 > "$OUT"++chmod 600 "$OUT"++++echo "$OUT"@@@ -25,7 +25,7 @@ donekill_matching() {local signal="$1"local pattern="$2"-- pgrep -f "$pattern" 2>/dev/null | while read -r pid; do++ (pgrep -f "$pattern" 2>/dev/null || true) | while read -r pid; doif [[ "$pid" != "$$" ]]; thenkill "-$signal" "$pid" 2>/dev/null || truefi@@@ -13,6 +13,21 @@ const textures = {trigger: "TOOLS/TOOLSTRIGGER",};++const useAuthenticatedJoin = process.env.OPENVIBE_USE_OV_JOIN === "1";++++const localHosts = {++ hub: "127.0.0.1",++ prophunt: "127.0.0.2",++ deathrun: "127.0.0.3",++ fortwars: "127.0.0.4",++ traitortown: "127.0.0.5",++};++++function joinCommand(mode, port) {++ if (useAuthenticatedJoin) return `ov_join ${mode}`;++ return `connect ${localHosts[mode] ?? localHosts.hub}:${port}`;++}++function id() {return nextId++;}@@@ -197,7 +212,6 @@ function propNpc(name, origin, angle = 180) {angles: `0 ${angle} 0`,model: "models/Humans/Group03/male_07.mdl",solid: "0",-- DefaultAnim: "idle_all_01",});}@@@ -348,7 +362,7 @@ function hub() {[pad.origin[0] - 128, pad.origin[1] - 128, 16],[pad.origin[0] + 128, pad.origin[1] + 128, 128],commandTarget,-- `ov_join ${pad.mode}`,++ joinCommand(pad.mode, pad.port),),];}),@@@ -417,7 +431,7 @@ function minigame(mapName, modeName, returnX = 0) {[returnX - 128, 580, 16],[returnX + 128, 836, 128],"cmd_return_hub",-- "ov_join hub",++ joinCommand("hub", 27015),),];@@@ -45,4 +45,5 @@ exec "$GE_PROTON/proton" waitforexitandrun \"$HL2_EXE" \-game "$GAME_DIR" \-console -dev -novid -sw -w 1280 -h 720 \++ -port 27115 -clientport 27105 \$CONNECT_ARGS@@@ -26,4 +26,6 @@ exec "$LAUNCHER" \-dev \-sw \-w 1280 \-- -h 720++ -h 720 \++ -port 27115 \++ -clientport 27105@@@ -17,6 +17,17 @@ SRCDS="${OPENVIBE_SRCDS:-$HOME/srcds/tf2}"MOD="$ROOT/game/openvibe.games"MAP_DELAY="${OPENVIBE_SRCDS_MAP_DELAY:-6}"++case "$MODE" in++ hub) DEFAULT_BIND_IP="127.0.0.1" ;;++ prophunt) DEFAULT_BIND_IP="127.0.0.2" ;;++ deathrun) DEFAULT_BIND_IP="127.0.0.3" ;;++ fortwars) DEFAULT_BIND_IP="127.0.0.4" ;;++ traitortown) DEFAULT_BIND_IP="127.0.0.5" ;;++ *) DEFAULT_BIND_IP="127.0.0.1" ;;++esac++++BIND_IP="${OPENVIBE_BIND_IP:-$DEFAULT_BIND_IP}"++if [[ ! -x "$SRCDS/srcds_linux64" ]]; thenecho "Missing 64-bit SRCDS at $SRCDS/srcds_linux64" >&2echo "Install Team Fortress 2 Dedicated Server AppID 232250 into $SRCDS." >&2@@@ -33,17 +44,28 @@ cd "$SRCDS"printf '243750\n' > "$SRCDS/steam_appid.txt"--export LD_LIBRARY_PATH="$MOD/bin/linux64:.:$SRCDS/bin/linux64:$SRCDS/bin:${LD_LIBRARY_PATH:-}"++COMPAT_LIB_DIR="${OPENVIBE_SRCDS_COMPAT_LIB_DIR:-/tmp/openvibe-srcds-compat-$USER}"++if [[ ! -e /lib/i386-linux-gnu/libtinfo.so.5 && -e /lib/i386-linux-gnu/libtinfo.so.6 ]]; then++ mkdir -p "$COMPAT_LIB_DIR"++ ln -sfn /lib/i386-linux-gnu/libtinfo.so.6 "$COMPAT_LIB_DIR/libtinfo.so.5"++fi++if [[ ! -e /lib/i386-linux-gnu/libncurses.so.5 && -e /lib/i386-linux-gnu/libncurses.so.6 ]]; then++ mkdir -p "$COMPAT_LIB_DIR"++ ln -sfn /lib/i386-linux-gnu/libncurses.so.6 "$COMPAT_LIB_DIR/libncurses.so.5"++fi++++export LD_LIBRARY_PATH="$COMPAT_LIB_DIR:$MOD/bin/linux64:.:$SRCDS/bin/linux64:$SRCDS/bin:${LD_LIBRARY_PATH:-}"server_cmd=(./srcds_linux64-game "$MOD"-console-usercon-- -condebug-- +ip 0.0.0.0-- +port "$PORT"++ -ip "$BIND_IP"++ -port "$PORT"++ +clientport "$((PORT + 1000))"+maxplayers "$MAXPLAYERS"++ +tv_port "$((PORT + 2000))"+exec "$CFG")@@@ -54,11 +76,16 @@ if [[ "${OPENVIBE_SRCDS_PIPE_MAP:-1}" == "1" ]]; thenprintf 'map %s\n' "$MAP"while sleep 3600; do :; done}++ filter_output() {++ sed -u \++ -e '/WARNING: Failed to load 32-bit libtinfo\.so\.5 or libncurses\.so\.5\./d' \++ -e '/Please install (lib32tinfo5 \/ ncurses-libs\.i686 \/ equivalent) to enable readline\./d'++ }if command -v script >/dev/null 2>&1; then-- map_input | script -qfec "$server_command" /dev/null++ map_input | script -qfec "$server_command" /dev/null | filter_outputelse-- map_input | "${server_cmd[@]}"++ map_input | "${server_cmd[@]}" | filter_outputfielseexec "${server_cmd[@]}" +map "$MAP"@@@ -25,12 +25,21 @@ if [[ -d "$ROOT/game/openvibe.games/logs" ]]; thenLOG_DIR="$ROOT/game/openvibe.games/logs"fi++case "$MODE" in++ hub) DEFAULT_PUBLIC_HOST="127.0.0.1" ;;++ prophunt) DEFAULT_PUBLIC_HOST="127.0.0.2" ;;++ deathrun) DEFAULT_PUBLIC_HOST="127.0.0.3" ;;++ fortwars) DEFAULT_PUBLIC_HOST="127.0.0.4" ;;++ traitortown) DEFAULT_PUBLIC_HOST="127.0.0.5" ;;++ *) DEFAULT_PUBLIC_HOST="127.0.0.1" ;;++esac++exec node "$ROOT/tools/ov-sidecar.mjs" \--server-id "$SERVER_ID" \--server-secret "${OPENVIBE_SERVER_SECRET:-dev-secret}" \--mode "$MODE" \--port "$PORT" \--max-players "$MAX_PLAYERS" \-- --host "${OPENVIBE_PUBLIC_HOST:-127.0.0.1}" \++ --host "${OPENVIBE_PUBLIC_HOST:-$DEFAULT_PUBLIC_HOST}" \--api-url "${OPENVIBE_API_URL:-http://127.0.0.1:3000}" \--log-dir "$LOG_DIR"@@@ -38,7 +38,7 @@ const server = await post("/v1/servers/register", {serverSecret,mode: "prophunt",mapName: "ph_openvibe_dev",-- publicHost: "127.0.0.1",++ publicHost: "127.0.0.2",port: 27016,maxPlayers: 24,});@@@ -11,6 +11,8 @@ run_check() {local map="$3"local log="/tmp/openvibe-srcds-$name.log"++ rm -f "$log"++set +etimeout "${OPENVIBE_SRCDS_SMOKE_TIMEOUT:-70}s" "$ROOT/tools/$script" >"$log" 2>&1local code=$?@@@ -34,7 +36,13 @@ run_check() {exit 1fi-- if ! grep -Eq "Started map \"$map\"|Spawn Server: $map|Loading map \"$map\"|SV_ActivateServer" "$log"; then++ if grep -Eiq "WARNING: Port|Socket bound to non-default|Failed to load 32-bit|CLocalizedStringTable::AddFile|Failed to load custom font|Hud element .*doesn't have an entry|Couldn't parse script sequence|Unknown command|Missing Vgui material|material \".*\" not found" "$log"; then++ echo "[srcds:$name] reported a startup log warning/error"++ grep -Ein "WARNING: Port|Socket bound to non-default|Failed to load 32-bit|CLocalizedStringTable::AddFile|Failed to load custom font|Hud element .*doesn't have an entry|Couldn't parse script sequence|Unknown command|Missing Vgui material|material \".*\" not found" "$log"++ exit 1++ fi++++ if ! grep -Eq "Started map \"$map\"|Spawn Server: $map|Loading map \"$map\"|SV_ActivateServer|ov_.*\.nut ready" "$log"; thenecho "[srcds:$name] did not reach map load for $map"tail -120 "$log"exit 1
@@ -17,8 +17,11 @@ game/openvibe.games/demoheader.tmp
@@ -17,8 +17,11 @@ game/openvibe.games/demoheader.tmp
game/openvibe.games/download/
game/openvibe.games/download/
game/openvibe.games/downloadlists/
game/openvibe.games/downloadlists/
game/openvibe.games/logs/
game/openvibe.games/logs/
-game/openvibe.games/materials/vgui/
+game/openvibe.games/materials/vgui/*
+!game/openvibe.games/materials/vgui/gfx/
+!game/openvibe.games/materials/vgui/gfx/**
game/openvibe.games/cfg/settings.scr
game/openvibe.games/cfg/settings.scr
+backups/
*.core
*.core
core
core
@@ -8,14 +8,14 @@
@@ -8,14 +8,14 @@
- Backend API starts and responds on `http://127.0.0.1:3000`.
- Backend API starts and responds on `http://127.0.0.1:3000`.
- PostgreSQL dev database starts through the repo tooling.
- PostgreSQL dev database starts through the repo tooling.
- Five SRCDS shards boot:
- Five SRCDS shards boot:
- - Hub: `ov_hub` on `27015`
- - Prop Hunt: `ph_openvibe_dev` on `27016`
- - Deathrun: `dr_openvibe_dev` on `27017`
- - Fort Wars: `fw_openvibe_dev` on `27018`
- - Traitor Town: `tt_openvibe_dev` on `27019`
+ - Hub: `ov_hub` on `127.0.0.1:27015`
+ - Prop Hunt: `ph_openvibe_dev` on `127.0.0.2:27016`
+ - Deathrun: `dr_openvibe_dev` on `127.0.0.3:27017`
+ - Fort Wars: `fw_openvibe_dev` on `127.0.0.4:27018`
+ - Traitor Town: `tt_openvibe_dev` on `127.0.0.5:27019`
- Sidecars register and heartbeat those servers with the backend.
- Sidecars register and heartbeat those servers with the backend.
- Travel requests return live local connect targets for every mode.
- Travel requests return live local connect targets for every mode.
-- Maps use `ov_join <mode>` portal commands instead of hardcoded connects.
+- Maps use direct local connect commands for the current Proton client fallback; regenerate with `OPENVIBE_USE_OV_JOIN=1` after rebuilding/loading the custom OpenVibe client DLL.
## Implemented Pieces
## Implemented Pieces
@@ -32,6 +32,9 @@
@@ -32,6 +32,9 @@
| Steam auth | Implemented route | Requires real `STEAM_WEB_API_KEY` and `STEAM_APP_ID` for production. |
| Steam auth | Implemented route | Requires real `STEAM_WEB_API_KEY` and `STEAM_APP_ID` for production. |
| CDN manifest | Implemented route | Requires real hosted storage under `openvibe.games` for production content. |
| CDN manifest | Implemented route | Requires real hosted storage under `openvibe.games` for production content. |
| Production infra | Scaffolded | Docker Compose, Docker Swarm, Kubernetes, nginx CDN config. |
| Production infra | Scaffolded | Docker Compose, Docker Swarm, Kubernetes, nginx CDN config. |
+| Parties/friend invites | Implemented | Create party, invite, accept, capacity-aware party travel. |
+| Moderation/audit | Implemented | Admin audit event write/list routes. |
+| Metrics/backups | Implemented | `/metrics` endpoint and `tools/backup-postgres.sh`. |
## Verified
## Verified
@@ -41,7 +44,7 @@ npm run build
@@ -41,7 +44,7 @@ npm run build
npm test
npm test
```
```
-Result: TypeScript build passed, 9 tests passed.
+Result: TypeScript build passed, 11 tests passed.
```bash
```bash
cd ~/src/openvibe-source
cd ~/src/openvibe-source
@@ -81,6 +84,6 @@ Before public deployment:
@@ -81,6 +84,6 @@ Before public deployment:
- Configure real Steam app identity and `STEAM_WEB_API_KEY`.
- Configure real Steam app identity and `STEAM_WEB_API_KEY`.
- Host cosmetic assets under the `openvibe.games` CDN path.
- Host cosmetic assets under the `openvibe.games` CDN path.
- Set real production secrets for API, Redis, PostgreSQL, and admin routes.
- Set real production secrets for API, Redis, PostgreSQL, and admin routes.
-- Add backups, metrics, health checks, log shipping, and deployment secret management.
+- Add alert routing, log shipping, deployment secret management, and regular restore drills.
- Expand each game mode from prototype rules into full production gameplay.
- Expand each game mode from prototype rules into full production gameplay.
- Add moderation/admin UI and anti-abuse controls.
- Add moderation/admin UI and anti-abuse controls.
@@ -46,13 +46,13 @@ openvibe-source/
@@ -46,13 +46,13 @@ openvibe-source/
## Game Modes
## Game Modes
-| Mode | Map | Port | Join command |
-| --- | --- | ---: | --- |
-| Hub | `ov_hub` | 27015 | `ov_join hub` |
-| Prop Hunt | `ph_openvibe_dev` | 27016 | `ov_join prophunt` |
-| Deathrun | `dr_openvibe_dev` | 27017 | `ov_join deathrun` |
-| Fort Wars | `fw_openvibe_dev` | 27018 | `ov_join fortwars` |
-| Traitor Town | `tt_openvibe_dev` | 27019 | `ov_join traitortown` |
+| Mode | Map | Local target | Authenticated command |
+| --- | --- | --- | --- |
+| Hub | `ov_hub` | `127.0.0.1:27015` | `ov_join hub` |
+| Prop Hunt | `ph_openvibe_dev` | `127.0.0.2:27016` | `ov_join prophunt` |
+| Deathrun | `dr_openvibe_dev` | `127.0.0.3:27017` | `ov_join deathrun` |
+| Fort Wars | `fw_openvibe_dev` | `127.0.0.4:27018` | `ov_join fortwars` |
+| Traitor Town | `tt_openvibe_dev` | `127.0.0.5:27019` | `ov_join traitortown` |
## Build And Test
## Build And Test
@@ -91,7 +91,7 @@ tools/dev-down.sh
@@ -91,7 +91,7 @@ tools/dev-down.sh
## Verified On June 28, 2026
## Verified On June 28, 2026
- Backend TypeScript build passed.
- Backend TypeScript build passed.
-- Backend Vitest suite passed: 9 tests.
+- Backend Vitest suite passed: 11 tests.
- Source SDK 2013 Linux64 build passed with OpenVibe C++ patch applied.
- Source SDK 2013 Linux64 build passed with OpenVibe C++ patch applied.
- SRCDS smoke test passed for all five maps.
- SRCDS smoke test passed for all five maps.
- Full dev stack registered all five servers and returned valid travel targets for every mode.
- Full dev stack registered all five servers and returned valid travel targets for every mode.
@@ -67,16 +67,18 @@ node tools/generate-dev-vmfs.mjs
@@ -67,16 +67,18 @@ node tools/generate-dev-vmfs.mjs
tools/compile-dev-maps-wine.sh
tools/compile-dev-maps-wine.sh
```
```
-Portal pads currently emit:
+Portal pads currently emit direct local connects for the Proton fallback:
```text
```text
-ov_join hub
-ov_join prophunt
-ov_join deathrun
-ov_join fortwars
-ov_join traitortown
+connect 127.0.0.1:27015
+connect 127.0.0.2:27016
+connect 127.0.0.3:27017
+connect 127.0.0.4:27018
+connect 127.0.0.5:27019
```
```
+Set `OPENVIBE_USE_OV_JOIN=1` before regenerating VMFs to bake authenticated `ov_join <mode>` portal commands for a rebuilt custom client DLL.
+
## Client / Server Commands
## Client / Server Commands
| Command | Side | Purpose |
| Command | Side | Purpose |
@@ -95,6 +97,7 @@ ov_join traitortown
@@ -95,6 +97,7 @@ ov_join traitortown
curl -s http://127.0.0.1:3000/health | jq
curl -s http://127.0.0.1:3000/health | jq
curl -s http://127.0.0.1:3000/v1/servers | jq
curl -s http://127.0.0.1:3000/v1/servers | jq
curl -s http://127.0.0.1:3000/v1/assets/manifest | jq
curl -s http://127.0.0.1:3000/v1/assets/manifest | jq
+curl -s http://127.0.0.1:3000/metrics
```
```
Travel request:
Travel request:
@@ -128,6 +131,13 @@ OPENVIBE_ADMIN_SECRET
@@ -128,6 +131,13 @@ OPENVIBE_ADMIN_SECRET
OPENVIBE_CDN_BASE_URL
OPENVIBE_CDN_BASE_URL
```
```
+## Backup
+
+```bash
+cd ~/src/openvibe-source
+tools/backup-postgres.sh
+```
+
## Common Fixes
## Common Fixes
| Issue | Fix |
| Issue | Fix |
@@ -24,14 +24,15 @@ Implemented and tested locally:
@@ -24,14 +24,15 @@ Implemented and tested locally:
- **Prop Hunt C++ disguise command** - `ov_prophunt_disguise <model>` swaps the player to an allowlisted prop model; `ov_prophunt_reset_disguise` restores the player model.
- **Prop Hunt C++ disguise command** - `ov_prophunt_disguise <model>` swaps the player to an allowlisted prop model; `ov_prophunt_reset_disguise` restores the player model.
- **Fort Wars C++ placement command** - `ov_fortwars_spawn <crate|barrel|fence|plate|concrete>` spawns allowlisted physics props during the build phase.
- **Fort Wars C++ placement command** - `ov_fortwars_spawn <crate|barrel|fence|plate|concrete>` spawns allowlisted physics props during the build phase.
- **VScript game logic** - Squirrel scripts for hub, Prop Hunt, Deathrun, Fort Wars, and Traitor Town prototypes.
- **VScript game logic** - Squirrel scripts for hub, Prop Hunt, Deathrun, Fort Wars, and Traitor Town prototypes.
-- **Portal maps** - generated VMFs and compiled BSPs use `ov_join` portals for hub/minigame travel.
+- **Portal maps** - generated VMFs and compiled BSPs use direct local connects for the current Proton fallback; set `OPENVIBE_USE_OV_JOIN=1` when generating maps for a rebuilt custom client DLL.
- **Sidecars** - tail SRCDS logs and bridge `[OV]` events to the backend API.
- **Sidecars** - tail SRCDS logs and bridge `[OV]` events to the backend API.
- **Production infra skeleton** - Docker Compose, Docker Swarm stack, and Kubernetes manifests for API, PostgreSQL, Redis, and CDN/static assets.
- **Production infra skeleton** - Docker Compose, Docker Swarm stack, and Kubernetes manifests for API, PostgreSQL, Redis, and CDN/static assets.
+- **Hardening routes** - party invites, capacity-aware party travel, moderation/audit events, Prometheus-style metrics, and backup tooling.
Verified locally on June 28, 2026:
Verified locally on June 28, 2026:
- `backend`: TypeScript build passes.
- `backend`: TypeScript build passes.
-- `backend`: 9 Vitest tests pass.
+- `backend`: 11 Vitest tests pass.
- Source SDK 2013 Linux64 build passes with the OpenVibe C++ patch applied.
- Source SDK 2013 Linux64 build passes with the OpenVibe C++ patch applied.
- SRCDS smoke test passes for all five maps: `ov_hub`, `ph_openvibe_dev`, `dr_openvibe_dev`, `fw_openvibe_dev`, `tt_openvibe_dev`.
- SRCDS smoke test passes for all five maps: `ov_hub`, `ph_openvibe_dev`, `dr_openvibe_dev`, `fw_openvibe_dev`, `tt_openvibe_dev`.
- Full dev stack registers all five servers and returns travel targets for hub, Prop Hunt, Deathrun, Fort Wars, and Traitor Town.
- Full dev stack registers all five servers and returns travel targets for hub, Prop Hunt, Deathrun, Fort Wars, and Traitor Town.
@@ -154,7 +155,7 @@ Source client
@@ -154,7 +155,7 @@ Source client
-> Steam auth ticket or dev auth
-> Steam auth ticket or dev auth
-> OpenVibe.Games API
-> OpenVibe.Games API
-> Hub SRCDS
-> Hub SRCDS
- -> ov_join portal pads
+ -> portal pads
-> Minigame SRCDS shards
-> Minigame SRCDS shards
OpenVibe.Games API
OpenVibe.Games API
@@ -165,6 +166,8 @@ OpenVibe.Games API
@@ -165,6 +166,8 @@ OpenVibe.Games API
Gameplay remains Source-style authoritative SRCDS instances. The MMO-style part is the shared OpenVibe.Games account, inventory, currency, travel, and server orchestration layer.
Gameplay remains Source-style authoritative SRCDS instances. The MMO-style part is the shared OpenVibe.Games account, inventory, currency, travel, and server orchestration layer.
+Local Proton builds use direct portal connects so the maps work even when the Windows client DLL has not been rebuilt. The authenticated C++ travel path is still available by regenerating maps with `OPENVIBE_USE_OV_JOIN=1`.
+
## API Endpoints
## API Endpoints
| Method | Path | Description |
| Method | Path | Description |
@@ -183,8 +186,16 @@ Gameplay remains Source-style authoritative SRCDS instances. The MMO-style part
@@ -183,8 +186,16 @@ Gameplay remains Source-style authoritative SRCDS instances. The MMO-style part
| `GET` | `/v1/servers` | List live servers |
| `GET` | `/v1/servers` | List live servers |
| `POST` | `/v1/travel/request` | Reserve travel token |
| `POST` | `/v1/travel/request` | Reserve travel token |
| `POST` | `/v1/travel/validate` | Consume travel token |
| `POST` | `/v1/travel/validate` | Consume travel token |
+| `POST` | `/v1/parties` | Create party |
+| `GET` | `/v1/parties?partyId=` | Get party members |
+| `POST` | `/v1/parties/invite` | Invite friend to party |
+| `POST` | `/v1/parties/invite/accept` | Accept party invite |
+| `POST` | `/v1/parties/travel` | Reserve one server for the whole party |
| `POST` | `/v1/matches/end` | Record match result + reward |
| `POST` | `/v1/matches/end` | Record match result + reward |
| `POST` | `/v1/admin/shop/items` | Admin: upsert shop item |
| `POST` | `/v1/admin/shop/items` | Admin: upsert shop item |
+| `POST` | `/v1/admin/audit/events` | Admin: record moderation/audit event |
+| `GET` | `/v1/admin/audit/events` | Admin: list audit events |
+| `GET` | `/metrics` | Prometheus-style service metrics |
## Production Configuration
## Production Configuration
@@ -204,6 +215,13 @@ Infrastructure templates live in:
@@ -204,6 +215,13 @@ Infrastructure templates live in:
- `infra/kubernetes/openvibe.yaml`
- `infra/kubernetes/openvibe.yaml`
- `infra/cdn/nginx.conf`
- `infra/cdn/nginx.conf`
+Local database backups:
+
+```bash
+cd ~/src/openvibe-source
+tools/backup-postgres.sh
+```
+
## Remaining Work
## Remaining Work
The foundation is running. The next real work is content and hardening:
The foundation is running. The next real work is content and hardening:
@@ -211,6 +229,5 @@ The foundation is running. The next real work is content and hardening:
@@ -211,6 +229,5 @@ The foundation is running. The next real work is content and hardening:
- Replace dev models/trails with real OpenVibe cosmetic assets.
- Replace dev models/trails with real OpenVibe cosmetic assets.
- Add real hosted CDN storage behind `openvibe.games`.
- Add real hosted CDN storage behind `openvibe.games`.
- Wire Steam production credentials and app identity.
- Wire Steam production credentials and app identity.
-- Add party travel, friend invites, and reservation capacity checks.
- Expand Prop Hunt, Deathrun, Fort Wars, and Traitor Town into full game rules.
- Expand Prop Hunt, Deathrun, Fort Wars, and Traitor Town into full game rules.
-- Add admin moderation, audit views, metrics, backups, and deployment secrets management.
+- Add full admin web views, alert routing, and deployment secrets management.
@@ -75,6 +75,38 @@ CREATE TABLE IF NOT EXISTS match_results (
@@ -75,6 +75,38 @@ CREATE TABLE IF NOT EXISTS match_results (
UNIQUE (match_id, steam_id)
UNIQUE (match_id, steam_id)
);
);
+CREATE TABLE IF NOT EXISTS parties (
+ party_id TEXT PRIMARY KEY,
+ leader_steam_id BIGINT NOT NULL REFERENCES players(steam_id) ON DELETE CASCADE,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
+);
+
+CREATE TABLE IF NOT EXISTS party_members (
+ party_id TEXT NOT NULL REFERENCES parties(party_id) ON DELETE CASCADE,
+ steam_id BIGINT NOT NULL REFERENCES players(steam_id) ON DELETE CASCADE,
+ joined_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+ PRIMARY KEY (party_id, steam_id)
+);
+
+CREATE TABLE IF NOT EXISTS party_invites (
+ invite_id TEXT PRIMARY KEY,
+ party_id TEXT NOT NULL REFERENCES parties(party_id) ON DELETE CASCADE,
+ invited_by_steam_id BIGINT NOT NULL REFERENCES players(steam_id) ON DELETE CASCADE,
+ invited_steam_id BIGINT NOT NULL REFERENCES players(steam_id) ON DELETE CASCADE,
+ status TEXT NOT NULL CHECK (status IN ('pending','accepted','declined','expired')),
+ expires_at TIMESTAMPTZ NOT NULL,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
+);
+
+CREATE TABLE IF NOT EXISTS audit_events (
+ audit_id TEXT PRIMARY KEY,
+ actor_steam_id BIGINT NOT NULL,
+ action TEXT NOT NULL,
+ target_steam_id BIGINT,
+ reason TEXT NOT NULL,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
+);
+
CREATE INDEX IF NOT EXISTS idx_game_servers_open
CREATE INDEX IF NOT EXISTS idx_game_servers_open
ON game_servers (mode, state, player_count, last_heartbeat);
ON game_servers (mode, state, player_count, last_heartbeat);
@@ -82,6 +114,12 @@ CREATE INDEX IF NOT EXISTS idx_join_tokens_valid
@@ -82,6 +114,12 @@ CREATE INDEX IF NOT EXISTS idx_join_tokens_valid
ON join_tokens (steam_id, server_id, expires_at)
ON join_tokens (steam_id, server_id, expires_at)
WHERE consumed_at IS NULL;
WHERE consumed_at IS NULL;
+CREATE INDEX IF NOT EXISTS idx_party_invites_invited
+ON party_invites (invited_steam_id, status, expires_at);
+
+CREATE INDEX IF NOT EXISTS idx_audit_events_created
+ON audit_events (created_at DESC);
+
INSERT INTO shop_items (item_id, item_type, display_name, description, asset_path, price)
INSERT INTO shop_items (item_id, item_type, display_name, description, asset_path, price)
VALUES
VALUES
('model_rebel', 'player_model', 'Rebel', 'Baseline resistance player model.', 'models/player/group03/male_07.mdl', 0),
('model_rebel', 'player_model', 'Rebel', 'Baseline resistance player model.', 'models/player/group03/male_07.mdl', 0),
@@ -311,4 +311,88 @@ describe("OpenVibe API vertical slice", () => {
@@ -311,4 +311,88 @@ describe("OpenVibe API vertical slice", () => {
expect(res.statusCode).toBe(403);
expect(res.statusCode).toBe(403);
await app.close();
await app.close();
});
});
+
+ it("supports party invites and capacity-aware party travel", async () => {
+ const app = await testApp();
+ const friendSteamId = "76561198000000003";
+
+ await app.inject({ method: "POST", url: "/v1/auth/dev",
+ payload: { steamId, displayName: "Party Leader" } });
+ await app.inject({ method: "POST", url: "/v1/auth/dev",
+ payload: { steamId: friendSteamId, displayName: "Party Friend" } });
+
+ await app.inject({ method: "POST", url: "/v1/servers/register",
+ payload: { serverId: "party-prophunt-1", serverSecret, mode: "prophunt",
+ mapName: "ph_openvibe_dev", publicHost: "127.0.0.1", port: 27016, maxPlayers: 1 } });
+ await app.inject({ method: "POST", url: "/v1/servers/register",
+ payload: { serverId: "party-prophunt-2", serverSecret, mode: "prophunt",
+ mapName: "ph_openvibe_dev", publicHost: "127.0.0.1", port: 27026, maxPlayers: 4 } });
+
+ const partyRes = await app.inject({
+ method: "POST",
+ url: "/v1/parties",
+ payload: { leaderSteamId: steamId },
+ });
+ expect(partyRes.statusCode).toBe(200);
+ const party = partyRes.json();
+
+ const inviteRes = await app.inject({
+ method: "POST",
+ url: "/v1/parties/invite",
+ payload: { partyId: party.partyId, invitedBySteamId: steamId, invitedSteamId: friendSteamId },
+ });
+ expect(inviteRes.statusCode).toBe(200);
+
+ const acceptRes = await app.inject({
+ method: "POST",
+ url: "/v1/parties/invite/accept",
+ payload: { inviteId: inviteRes.json().inviteId, steamId: friendSteamId },
+ });
+ expect(acceptRes.json().members).toHaveLength(2);
+
+ const travelRes = await app.inject({
+ method: "POST",
+ url: "/v1/parties/travel",
+ payload: { partyId: party.partyId, leaderSteamId: steamId, mode: "prophunt" },
+ });
+ expect(travelRes.statusCode).toBe(200);
+ expect(travelRes.json().serverId).toBe("party-prophunt-2");
+ expect(travelRes.json().reservations).toHaveLength(2);
+
+ await app.close();
+ });
+
+ it("records and lists admin audit events", async () => {
+ const adminSecret = "test-admin-secret";
+ const app = await createApp({
+ repository: new MemoryOpenVibeRepository(),
+ devAuthEnabled: true,
+ adminSecret,
+ });
+ await app.ready();
+
+ const event = await app.inject({
+ method: "POST",
+ url: "/v1/admin/audit/events",
+ headers: { "x-admin-secret": adminSecret },
+ payload: {
+ actorSteamId: steamId,
+ action: "moderation.note",
+ targetSteamId: "76561198000000004",
+ reason: "Testing moderation audit trail.",
+ },
+ });
+ expect(event.statusCode).toBe(200);
+ expect(event.json().action).toBe("moderation.note");
+
+ const list = await app.inject({
+ method: "GET",
+ url: "/v1/admin/audit/events?limit=10",
+ headers: { "x-admin-secret": adminSecret },
+ });
+ expect(list.statusCode).toBe(200);
+ expect(list.json().events).toHaveLength(1);
+
+ await app.close();
+ });
});
});
@@ -5,13 +5,20 @@ import { OpenVibeRepository } from "./domain.js";
@@ -5,13 +5,20 @@ import { OpenVibeRepository } from "./domain.js";
import {
import {
authDevSchema,
authDevSchema,
authSteamSchema,
authSteamSchema,
+ acceptPartyInviteSchema,
+ auditEventSchema,
+ auditQuerySchema,
buyItemSchema,
buyItemSchema,
+ createPartySchema,
equipItemSchema,
equipItemSchema,
+ getPartyQuerySchema,
getMeQuerySchema,
getMeQuerySchema,
heartbeatSchema,
heartbeatSchema,
leaderboardQuerySchema,
leaderboardQuerySchema,
listServersQuerySchema,
listServersQuerySchema,
matchEndSchema,
matchEndSchema,
+ partyInviteSchema,
+ partyTravelSchema,
registerServerSchema,
registerServerSchema,
travelRequestSchema,
travelRequestSchema,
upsertShopItemSchema,
upsertShopItemSchema,
@@ -226,6 +233,35 @@ export async function createApp(options: AppOptions): Promise<FastifyInstance> {
@@ -226,6 +233,35 @@ export async function createApp(options: AppOptions): Promise<FastifyInstance> {
return reservation;
return reservation;
});
});
+ app.post("/v1/parties", async (request) => {
+ const body = createPartySchema.parse(request.body);
+ return options.repository.createParty(body);
+ });
+
+ app.get("/v1/parties", async (request, reply) => {
+ const query = getPartyQuerySchema.parse(request.query);
+ const party = await options.repository.getParty(query.partyId);
+ if (!party) return reply.code(404).send({ error: "party_not_found" });
+ return party;
+ });
+
+ app.post("/v1/parties/invite", async (request) => {
+ const body = partyInviteSchema.parse(request.body);
+ return options.repository.inviteToParty(body);
+ });
+
+ app.post("/v1/parties/invite/accept", async (request) => {
+ const body = acceptPartyInviteSchema.parse(request.body);
+ return options.repository.acceptPartyInvite(body);
+ });
+
+ app.post("/v1/parties/travel", async (request, reply) => {
+ const body = partyTravelSchema.parse(request.body);
+ const reservation = await options.repository.reservePartyTravel(body);
+ if (!reservation) return reply.code(404).send({ error: "no_server_with_party_capacity" });
+ return reservation;
+ });
+
app.post("/v1/travel/validate", async (request) => {
app.post("/v1/travel/validate", async (request) => {
const body = validateJoinTokenSchema.parse(request.body);
const body = validateJoinTokenSchema.parse(request.body);
return options.repository.validateJoinToken(body);
return options.repository.validateJoinToken(body);
@@ -257,6 +293,34 @@ export async function createApp(options: AppOptions): Promise<FastifyInstance> {
@@ -257,6 +293,34 @@ export async function createApp(options: AppOptions): Promise<FastifyInstance> {
return options.repository.upsertShopItem(body);
return options.repository.upsertShopItem(body);
});
});
+ app.post("/v1/admin/audit/events", async (request, reply) => {
+ if (!requireAdmin(request, reply)) return;
+ const body = auditEventSchema.parse(request.body);
+ return options.repository.recordAuditEvent(body);
+ });
+
+ app.get("/v1/admin/audit/events", async (request, reply) => {
+ if (!requireAdmin(request, reply)) return;
+ const query = auditQuerySchema.parse(request.query);
+ return { events: await options.repository.listAuditEvents(query) };
+ });
+
+ app.get("/metrics", async (_request, reply) => {
+ const servers = await options.repository.listServers();
+ const open = servers.filter((server) => server.state === "open").length;
+ const players = servers.reduce((sum, server) => sum + server.playerCount, 0);
+ reply.type("text/plain; version=0.0.4");
+ return [
+ "# HELP openvibe_servers_open Number of open OpenVibe Source servers.",
+ "# TYPE openvibe_servers_open gauge",
+ `openvibe_servers_open ${open}`,
+ "# HELP openvibe_players_online Current players reported by live servers.",
+ "# TYPE openvibe_players_online gauge",
+ `openvibe_players_online ${players}`,
+ "",
+ ].join("\n");
+ });
+
app.addHook("onClose", async () => {
app.addHook("onClose", async () => {
await options.sessionStore?.close?.();
await options.sessionStore?.close?.();
});
});
@@ -82,6 +82,45 @@ export interface TravelReservation {
@@ -82,6 +82,45 @@ export interface TravelReservation {
expiresAt: string;
expiresAt: string;
}
}
+export interface PartyMember {
+ steamId: string;
+ displayName: string;
+ leader: boolean;
+ joinedAt: string;
+}
+
+export interface Party {
+ partyId: string;
+ leaderSteamId: string;
+ members: PartyMember[];
+}
+
+export interface PartyInvite {
+ inviteId: string;
+ partyId: string;
+ invitedSteamId: string;
+ invitedBySteamId: string;
+ status: "pending" | "accepted" | "declined" | "expired";
+ expiresAt: string;
+}
+
+export interface PartyTravelReservation {
+ partyId: string;
+ mode: GameMode;
+ serverId: string;
+ connect: string;
+ reservations: TravelReservation[];
+}
+
+export interface AuditEvent {
+ auditId: string;
+ actorSteamId: string;
+ action: string;
+ targetSteamId: string | null;
+ reason: string;
+ createdAt: string;
+}
+
export interface JoinTokenValidation {
export interface JoinTokenValidation {
valid: boolean;
valid: boolean;
serverId?: string;
serverId?: string;
@@ -120,6 +159,19 @@ export interface OpenVibeRepository {
@@ -120,6 +159,19 @@ export interface OpenVibeRepository {
heartbeat(input: HeartbeatInput): Promise<GameServer | null>;
heartbeat(input: HeartbeatInput): Promise<GameServer | null>;
listServers(mode?: GameMode): Promise<GameServer[]>;
listServers(mode?: GameMode): Promise<GameServer[]>;
reserveTravel(input: { steamId: string; mode: GameMode }): Promise<TravelReservation | null>;
reserveTravel(input: { steamId: string; mode: GameMode }): Promise<TravelReservation | null>;
+ createParty(input: { leaderSteamId: string }): Promise<Party>;
+ inviteToParty(input: {
+ partyId: string;
+ invitedBySteamId: string;
+ invitedSteamId: string;
+ }): Promise<PartyInvite>;
+ acceptPartyInvite(input: { inviteId: string; steamId: string }): Promise<Party>;
+ getParty(partyId: string): Promise<Party | null>;
+ reservePartyTravel(input: {
+ partyId: string;
+ leaderSteamId: string;
+ mode: GameMode;
+ }): Promise<PartyTravelReservation | null>;
validateJoinToken(input: {
validateJoinToken(input: {
token: string;
token: string;
steamId: string;
steamId: string;
@@ -128,4 +180,11 @@ export interface OpenVibeRepository {
@@ -128,4 +180,11 @@ export interface OpenVibeRepository {
recordMatchReward(input: MatchRewardInput): Promise<PlayerProfile | null>;
recordMatchReward(input: MatchRewardInput): Promise<PlayerProfile | null>;
getLeaderboard(options: { limit: number; mode?: GameMode }): Promise<LeaderboardEntry[]>;
getLeaderboard(options: { limit: number; mode?: GameMode }): Promise<LeaderboardEntry[]>;
upsertShopItem(input: UpsertShopItemInput): Promise<ShopItem>;
upsertShopItem(input: UpsertShopItemInput): Promise<ShopItem>;
+ recordAuditEvent(input: {
+ actorSteamId: string;
+ action: string;
+ targetSteamId?: string | null;
+ reason: string;
+ }): Promise<AuditEvent>;
+ listAuditEvents(options: { limit: number }): Promise<AuditEvent[]>;
}
}
@@ -4,10 +4,14 @@ import {
@@ -4,10 +4,14 @@ import {
GameServer,
GameServer,
HeartbeatInput,
HeartbeatInput,
InventoryItem,
InventoryItem,
+ AuditEvent,
JoinTokenValidation,
JoinTokenValidation,
LeaderboardEntry,
LeaderboardEntry,
MatchRewardInput,
MatchRewardInput,
OpenVibeRepository,
OpenVibeRepository,
+ Party,
+ PartyInvite,
+ PartyTravelReservation,
Player,
Player,
PlayerProfile,
PlayerProfile,
RegisterServerInput,
RegisterServerInput,
@@ -29,6 +33,13 @@ interface StoredToken {
@@ -29,6 +33,13 @@ interface StoredToken {
consumedAt: string | null;
consumedAt: string | null;
}
}
+interface StoredParty {
+ partyId: string;
+ leaderSteamId: string;
+ members: Set<string>;
+ joinedAt: Map<string, string>;
+}
+
const seedShop: ShopItem[] = [
const seedShop: ShopItem[] = [
{
{
itemId: "model_rebel",
itemId: "model_rebel",
@@ -56,6 +67,9 @@ export class MemoryOpenVibeRepository implements OpenVibeRepository {
@@ -56,6 +67,9 @@ export class MemoryOpenVibeRepository implements OpenVibeRepository {
private inventory = new Map<string, Set<string>>();
private inventory = new Map<string, Set<string>>();
private servers = new Map<string, StoredServer>();
private servers = new Map<string, StoredServer>();
private tokens = new Map<string, StoredToken>();
private tokens = new Map<string, StoredToken>();
+ private parties = new Map<string, StoredParty>();
+ private invites = new Map<string, PartyInvite>();
+ private auditEvents: AuditEvent[] = [];
private rewardKeys = new Set<string>();
private rewardKeys = new Set<string>();
async upsertDevPlayer(input: { steamId: string; displayName: string }): Promise<PlayerProfile> {
async upsertDevPlayer(input: { steamId: string; displayName: string }): Promise<PlayerProfile> {
@@ -194,6 +208,110 @@ export class MemoryOpenVibeRepository implements OpenVibeRepository {
@@ -194,6 +208,110 @@ export class MemoryOpenVibeRepository implements OpenVibeRepository {
};
};
}
}
+ async createParty(input: { leaderSteamId: string }): Promise<Party> {
+ this.requirePlayer(input.leaderSteamId);
+ const now = new Date().toISOString();
+ const party: StoredParty = {
+ partyId: nanoid(16),
+ leaderSteamId: input.leaderSteamId,
+ members: new Set([input.leaderSteamId]),
+ joinedAt: new Map([[input.leaderSteamId, now]]),
+ };
+ this.parties.set(party.partyId, party);
+ return this.publicParty(party);
+ }
+
+ async inviteToParty(input: {
+ partyId: string;
+ invitedBySteamId: string;
+ invitedSteamId: string;
+ }): Promise<PartyInvite> {
+ const party = this.requireParty(input.partyId);
+ this.requirePlayer(input.invitedBySteamId);
+ this.requirePlayer(input.invitedSteamId);
+ if (!party.members.has(input.invitedBySteamId)) throw new RepositoryError("not_party_member", 403);
+
+ const invite: PartyInvite = {
+ inviteId: nanoid(16),
+ partyId: input.partyId,
+ invitedBySteamId: input.invitedBySteamId,
+ invitedSteamId: input.invitedSteamId,
+ status: "pending",
+ expiresAt: new Date(Date.now() + 300_000).toISOString(),
+ };
+ this.invites.set(invite.inviteId, invite);
+ return { ...invite };
+ }
+
+ async acceptPartyInvite(input: { inviteId: string; steamId: string }): Promise<Party> {
+ const invite = this.invites.get(input.inviteId);
+ if (!invite || invite.status !== "pending") throw new RepositoryError("invite_not_found", 404);
+ if (invite.invitedSteamId !== input.steamId) throw new RepositoryError("invite_forbidden", 403);
+ if (new Date(invite.expiresAt).getTime() <= Date.now()) {
+ invite.status = "expired";
+ throw new RepositoryError("invite_expired", 409);
+ }
+
+ const party = this.requireParty(invite.partyId);
+ party.members.add(input.steamId);
+ party.joinedAt.set(input.steamId, new Date().toISOString());
+ invite.status = "accepted";
+ return this.publicParty(party);
+ }
+
+ async getParty(partyId: string): Promise<Party | null> {
+ const party = this.parties.get(partyId);
+ return party ? this.publicParty(party) : null;
+ }
+
+ async reservePartyTravel(input: {
+ partyId: string;
+ leaderSteamId: string;
+ mode: GameMode;
+ }): Promise<PartyTravelReservation | null> {
+ const party = this.requireParty(input.partyId);
+ if (party.leaderSteamId !== input.leaderSteamId) {
+ throw new RepositoryError("party_leader_required", 403);
+ }
+
+ const members = [...party.members];
+ const server = [...this.servers.values()]
+ .filter((candidate) => candidate.mode === input.mode)
+ .filter((candidate) => candidate.state === "open")
+ .filter((candidate) => candidate.maxPlayers - candidate.playerCount >= members.length)
+ .sort((a, b) => b.playerCount - a.playerCount)[0];
+
+ if (!server) return null;
+
+ const reservations = members.map((steamId) => {
+ const expiresAt = new Date(Date.now() + 90_000).toISOString();
+ const token = nanoid(32);
+ this.tokens.set(token, {
+ token,
+ steamId,
+ serverId: server.serverId,
+ mode: input.mode,
+ expiresAt,
+ consumedAt: null,
+ });
+ return {
+ mode: input.mode,
+ serverId: server.serverId,
+ connect: `${server.publicHost}:${server.port}`,
+ joinToken: token,
+ expiresAt,
+ };
+ });
+
+ return {
+ partyId: input.partyId,
+ mode: input.mode,
+ serverId: server.serverId,
+ connect: `${server.publicHost}:${server.port}`,
+ reservations,
+ };
+ }
+
async validateJoinToken(input: {
async validateJoinToken(input: {
token: string;
token: string;
steamId: string;
steamId: string;
@@ -273,11 +391,33 @@ export class MemoryOpenVibeRepository implements OpenVibeRepository {
@@ -273,11 +391,33 @@ export class MemoryOpenVibeRepository implements OpenVibeRepository {
return item;
return item;
}
}
+ private requireParty(partyId: string): StoredParty {
+ const party = this.parties.get(partyId);
+ if (!party) throw new RepositoryError("party_not_found", 404);
+ return party;
+ }
+
private publicServer(server: StoredServer): GameServer {
private publicServer(server: StoredServer): GameServer {
const { serverSecret: _serverSecret, ...safe } = server;
const { serverSecret: _serverSecret, ...safe } = server;
return { ...safe };
return { ...safe };
}
}
+ private publicParty(party: StoredParty): Party {
+ return {
+ partyId: party.partyId,
+ leaderSteamId: party.leaderSteamId,
+ members: [...party.members].map((steamId) => {
+ const player = this.requirePlayer(steamId);
+ return {
+ steamId,
+ displayName: player.displayName,
+ leader: steamId === party.leaderSteamId,
+ joinedAt: party.joinedAt.get(steamId) ?? new Date(0).toISOString(),
+ };
+ }),
+ };
+ }
+
async getLeaderboard(options: { limit: number; mode?: GameMode }): Promise<LeaderboardEntry[]> {
async getLeaderboard(options: { limit: number; mode?: GameMode }): Promise<LeaderboardEntry[]> {
const players = [...this.players.values()];
const players = [...this.players.values()];
const sorted = players
const sorted = players
@@ -298,6 +438,28 @@ export class MemoryOpenVibeRepository implements OpenVibeRepository {
@@ -298,6 +438,28 @@ export class MemoryOpenVibeRepository implements OpenVibeRepository {
this.shop.set(item.itemId, item);
this.shop.set(item.itemId, item);
return { ...item };
return { ...item };
}
}
+
+ async recordAuditEvent(input: {
+ actorSteamId: string;
+ action: string;
+ targetSteamId?: string | null;
+ reason: string;
+ }): Promise<AuditEvent> {
+ const event: AuditEvent = {
+ auditId: nanoid(16),
+ actorSteamId: input.actorSteamId,
+ action: input.action,
+ targetSteamId: input.targetSteamId ?? null,
+ reason: input.reason,
+ createdAt: new Date().toISOString(),
+ };
+ this.auditEvents.unshift(event);
+ return { ...event };
+ }
+
+ async listAuditEvents(options: { limit: number }): Promise<AuditEvent[]> {
+ return this.auditEvents.slice(0, options.limit).map((event) => ({ ...event }));
+ }
}
}
export class RepositoryError extends Error {
export class RepositoryError extends Error {
@@ -4,10 +4,14 @@ import {
@@ -4,10 +4,14 @@ import {
GameServer,
GameServer,
HeartbeatInput,
HeartbeatInput,
InventoryItem,
InventoryItem,
+ AuditEvent,
JoinTokenValidation,
JoinTokenValidation,
LeaderboardEntry,
LeaderboardEntry,
MatchRewardInput,
MatchRewardInput,
OpenVibeRepository,
OpenVibeRepository,
+ Party,
+ PartyInvite,
+ PartyTravelReservation,
Player,
Player,
PlayerProfile,
PlayerProfile,
RegisterServerInput,
RegisterServerInput,
@@ -316,6 +320,165 @@ export class PgOpenVibeRepository implements OpenVibeRepository {
@@ -316,6 +320,165 @@ export class PgOpenVibeRepository implements OpenVibeRepository {
};
};
}
}
+ async createParty(input: { leaderSteamId: string }): Promise<Party> {
+ const partyId = nanoid(16);
+ const client = await this.pool.connect();
+ try {
+ await client.query("BEGIN");
+ await client.query(
+ "INSERT INTO parties (party_id, leader_steam_id) VALUES ($1, $2)",
+ [partyId, input.leaderSteamId],
+ );
+ await client.query(
+ "INSERT INTO party_members (party_id, steam_id) VALUES ($1, $2)",
+ [partyId, input.leaderSteamId],
+ );
+ await client.query("COMMIT");
+ } catch (error) {
+ await client.query("ROLLBACK");
+ throw error;
+ } finally {
+ client.release();
+ }
+ const party = await this.getParty(partyId);
+ if (!party) throw new RepositoryError("party_not_found", 404);
+ return party;
+ }
+
+ async inviteToParty(input: {
+ partyId: string;
+ invitedBySteamId: string;
+ invitedSteamId: string;
+ }): Promise<PartyInvite> {
+ const member = await this.pool.query(
+ "SELECT 1 FROM party_members WHERE party_id = $1 AND steam_id = $2",
+ [input.partyId, input.invitedBySteamId],
+ );
+ if (member.rowCount !== 1) throw new RepositoryError("not_party_member", 403);
+
+ const inviteId = nanoid(16);
+ const result = await this.pool.query(
+ `
+ INSERT INTO party_invites (
+ invite_id, party_id, invited_by_steam_id, invited_steam_id, status, expires_at
+ )
+ VALUES ($1, $2, $3, $4, 'pending', now() + interval '5 minutes')
+ RETURNING invite_id, party_id, invited_by_steam_id, invited_steam_id, status, expires_at
+ `,
+ [inviteId, input.partyId, input.invitedBySteamId, input.invitedSteamId],
+ );
+ return mapPartyInvite(result.rows[0]);
+ }
+
+ async acceptPartyInvite(input: { inviteId: string; steamId: string }): Promise<Party> {
+ const client = await this.pool.connect();
+ let partyId = "";
+ try {
+ await client.query("BEGIN");
+ const invite = await client.query(
+ `
+ UPDATE party_invites
+ SET status = 'accepted'
+ WHERE invite_id = $1
+ AND invited_steam_id = $2
+ AND status = 'pending'
+ AND expires_at > now()
+ RETURNING party_id
+ `,
+ [input.inviteId, input.steamId],
+ );
+ if (invite.rowCount !== 1) throw new RepositoryError("invite_not_found", 404);
+ partyId = String(invite.rows[0].party_id);
+ await client.query(
+ `
+ INSERT INTO party_members (party_id, steam_id)
+ VALUES ($1, $2)
+ ON CONFLICT DO NOTHING
+ `,
+ [partyId, input.steamId],
+ );
+ await client.query("COMMIT");
+ } catch (error) {
+ await client.query("ROLLBACK");
+ throw error;
+ } finally {
+ client.release();
+ }
+ const party = await this.getParty(partyId);
+ if (!party) throw new RepositoryError("party_not_found", 404);
+ return party;
+ }
+
+ async getParty(partyId: string): Promise<Party | null> {
+ const result = await this.pool.query(
+ `
+ SELECT p.party_id, p.leader_steam_id, pm.steam_id, pm.joined_at, pl.display_name
+ FROM parties p
+ JOIN party_members pm ON pm.party_id = p.party_id
+ JOIN players pl ON pl.steam_id = pm.steam_id
+ WHERE p.party_id = $1
+ ORDER BY pm.joined_at
+ `,
+ [partyId],
+ );
+ if (result.rowCount === 0) return null;
+ return mapParty(result.rows);
+ }
+
+ async reservePartyTravel(input: {
+ partyId: string;
+ leaderSteamId: string;
+ mode: GameMode;
+ }): Promise<PartyTravelReservation | null> {
+ const party = await this.getParty(input.partyId);
+ if (!party) throw new RepositoryError("party_not_found", 404);
+ if (party.leaderSteamId !== input.leaderSteamId) throw new RepositoryError("party_leader_required", 403);
+
+ const serverResult = await this.pool.query(
+ `
+ SELECT server_id, public_host, port
+ FROM game_servers
+ WHERE mode = $1
+ AND state = 'open'
+ AND max_players - player_count >= $2
+ AND last_heartbeat > now() - interval '60 seconds'
+ ORDER BY player_count DESC, last_heartbeat DESC
+ LIMIT 1
+ `,
+ [input.mode, party.members.length],
+ );
+ if (serverResult.rowCount !== 1) return null;
+
+ const server = serverResult.rows[0];
+ const reservations: TravelReservation[] = [];
+ for (const member of party.members) {
+ const token = nanoid(32);
+ const tokenResult = await this.pool.query(
+ `
+ INSERT INTO join_tokens (token, steam_id, server_id, mode, expires_at)
+ VALUES ($1, $2, $3, $4, now() + interval '90 seconds')
+ RETURNING expires_at
+ `,
+ [token, member.steamId, server.server_id, input.mode],
+ );
+ reservations.push({
+ mode: input.mode,
+ serverId: String(server.server_id),
+ connect: `${server.public_host}:${server.port}`,
+ joinToken: token,
+ expiresAt: new Date(tokenResult.rows[0].expires_at).toISOString(),
+ });
+ }
+
+ return {
+ partyId: input.partyId,
+ mode: input.mode,
+ serverId: String(server.server_id),
+ connect: `${server.public_host}:${server.port}`,
+ reservations,
+ };
+ }
+
async validateJoinToken(input: {
async validateJoinToken(input: {
token: string;
token: string;
steamId: string;
steamId: string;
@@ -456,6 +619,36 @@ export class PgOpenVibeRepository implements OpenVibeRepository {
@@ -456,6 +619,36 @@ export class PgOpenVibeRepository implements OpenVibeRepository {
);
);
return mapShopItem(result.rows[0]);
return mapShopItem(result.rows[0]);
}
}
+
+ async recordAuditEvent(input: {
+ actorSteamId: string;
+ action: string;
+ targetSteamId?: string | null;
+ reason: string;
+ }): Promise<AuditEvent> {
+ const result = await this.pool.query(
+ `
+ INSERT INTO audit_events (audit_id, actor_steam_id, action, target_steam_id, reason)
+ VALUES ($1, $2, $3, $4, $5)
+ RETURNING audit_id, actor_steam_id, action, target_steam_id, reason, created_at
+ `,
+ [nanoid(16), input.actorSteamId, input.action, input.targetSteamId ?? null, input.reason],
+ );
+ return mapAuditEvent(result.rows[0]);
+ }
+
+ async listAuditEvents(options: { limit: number }): Promise<AuditEvent[]> {
+ const result = await this.pool.query(
+ `
+ SELECT audit_id, actor_steam_id, action, target_steam_id, reason, created_at
+ FROM audit_events
+ ORDER BY created_at DESC
+ LIMIT $1
+ `,
+ [options.limit],
+ );
+ return result.rows.map(mapAuditEvent);
+ }
}
}
function mapPlayer(row: Record<string, unknown>): Player {
function mapPlayer(row: Record<string, unknown>): Player {
@@ -501,3 +694,43 @@ function mapServer(row: Record<string, unknown>): GameServer {
@@ -501,3 +694,43 @@ function mapServer(row: Record<string, unknown>): GameServer {
lastHeartbeat: new Date(row.last_heartbeat as string | Date).toISOString(),
lastHeartbeat: new Date(row.last_heartbeat as string | Date).toISOString(),
};
};
}
}
+
+function mapPartyInvite(row: Record<string, unknown>): PartyInvite {
+ return {
+ inviteId: String(row.invite_id),
+ partyId: String(row.party_id),
+ invitedBySteamId: String(row.invited_by_steam_id),
+ invitedSteamId: String(row.invited_steam_id),
+ status: row.status as PartyInvite["status"],
+ expiresAt: new Date(row.expires_at as string | Date).toISOString(),
+ };
+}
+
+function mapParty(rows: Record<string, unknown>[]): Party {
+ const first = rows[0];
+ const leaderSteamId = String(first.leader_steam_id);
+ return {
+ partyId: String(first.party_id),
+ leaderSteamId,
+ members: rows.map((row) => {
+ const steamId = String(row.steam_id);
+ return {
+ steamId,
+ displayName: String(row.display_name),
+ leader: steamId === leaderSteamId,
+ joinedAt: new Date(row.joined_at as string | Date).toISOString(),
+ };
+ }),
+ };
+}
+
+function mapAuditEvent(row: Record<string, unknown>): AuditEvent {
+ return {
+ auditId: String(row.audit_id),
+ actorSteamId: String(row.actor_steam_id),
+ action: String(row.action),
+ targetSteamId: row.target_steam_id ? String(row.target_steam_id) : null,
+ reason: String(row.reason),
+ createdAt: new Date(row.created_at as string | Date).toISOString(),
+ };
+}
@@ -56,6 +56,31 @@ export const travelRequestSchema = z.object({
@@ -56,6 +56,31 @@ export const travelRequestSchema = z.object({
mode,
mode,
});
});
+export const createPartySchema = z.object({
+ leaderSteamId: steamId,
+});
+
+export const partyInviteSchema = z.object({
+ partyId: z.string().min(8).max(64),
+ invitedBySteamId: steamId,
+ invitedSteamId: steamId,
+});
+
+export const acceptPartyInviteSchema = z.object({
+ inviteId: z.string().min(8).max(64),
+ steamId,
+});
+
+export const getPartyQuerySchema = z.object({
+ partyId: z.string().min(8).max(64),
+});
+
+export const partyTravelSchema = z.object({
+ partyId: z.string().min(8).max(64),
+ leaderSteamId: steamId,
+ mode,
+});
+
export const validateJoinTokenSchema = z.object({
export const validateJoinTokenSchema = z.object({
token: z.string().min(16).max(128),
token: z.string().min(16).max(128),
steamId,
steamId,
@@ -87,3 +112,14 @@ export const upsertShopItemSchema = z.object({
@@ -87,3 +112,14 @@ export const upsertShopItemSchema = z.object({
price: z.number().int().min(0).max(100_000),
price: z.number().int().min(0).max(100_000),
enabled: z.boolean().default(true),
enabled: z.boolean().default(true),
});
});
+
+export const auditEventSchema = z.object({
+ actorSteamId: steamId,
+ action: z.string().trim().min(1).max(80),
+ targetSteamId: steamId.optional(),
+ reason: z.string().trim().min(1).max(500),
+});
+
+export const auditQuerySchema = z.object({
+ limit: z.coerce.number().int().min(1).max(200).default(50),
+});
@@ -69,20 +69,22 @@ npm run build
@@ -69,20 +69,22 @@ npm run build
npm test
npm test
```
```
-Current result: TypeScript build passes and 9 tests pass.
+Current result: TypeScript build passes and 11 tests pass.
## Maps And SRCDS
## Maps And SRCDS
-The VMF generator now emits portal commands using `ov_join`:
+The VMF generator currently emits direct local connects for the Proton fallback:
```text
```text
-ov_join hub
-ov_join prophunt
-ov_join deathrun
-ov_join fortwars
-ov_join traitortown
+connect 127.0.0.1:27015
+connect 127.0.0.2:27016
+connect 127.0.0.3:27017
+connect 127.0.0.4:27018
+connect 127.0.0.5:27019
```
```
+Set `OPENVIBE_USE_OV_JOIN=1` while regenerating VMFs to bake authenticated `ov_join <mode>` commands.
+
Compiled and smoke-tested BSPs:
Compiled and smoke-tested BSPs:
```text
```text
@@ -87,7 +87,7 @@ VScript cannot make HTTP requests directly. The sidecar bridges the gap:
@@ -87,7 +87,7 @@ VScript cannot make HTTP requests directly. The sidecar bridges the gap:
## C++ Travel Flow
## C++ Travel Flow
-Portal pads now call `ov_join <mode>` instead of hardcoded `connect` commands:
+The authenticated travel flow is implemented for rebuilt OpenVibe client DLLs:
```text
```text
1. Player steps on portal pad.
1. Player steps on portal pad.
@@ -99,6 +99,8 @@ Portal pads now call `ov_join <mode>` instead of hardcoded `connect` commands:
@@ -99,6 +99,8 @@ Portal pads now call `ov_join <mode>` instead of hardcoded `connect` commands:
7. Server C++ validates the token with `/v1/travel/validate`.
7. Server C++ validates the token with `/v1/travel/validate`.
```
```
+The checked-in dev BSPs currently use direct local `connect` commands as a Proton fallback. Regenerate VMFs with `OPENVIBE_USE_OV_JOIN=1` to bake the authenticated path into portal pads.
+
## GameDLL Feature Hooks
## GameDLL Feature Hooks
Tracked C++ patch sources live under `sdk/openvibe/` and are copied into the local Valve SDK checkout by `tools/apply-openvibe-sdk.sh`.
Tracked C++ patch sources live under `sdk/openvibe/` and are copied into the local Valve SDK checkout by `tools/apply-openvibe-sdk.sh`.
new file mode 100644
new file mode 100644
@@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
+// OpenVibe: Source client fallback aliases.
+//
+// The Linux64 SDK build provides real C++ implementations for these commands.
+// Proton/Windows clients may still load older DLLs until a Windows GameDLL is
+// built, so these aliases prevent noisy "Unknown command" console output.
+alias "openvibe_menu" "echo OpenVibe: Source menu is available through GameMenu.res or the launcher."
+alias "ov_menu" "openvibe_menu"
+alias "ov_join" "echo OpenVibe: authenticated ov_join requires the custom OpenVibe client DLL."
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+Welcome to OpenVibe: Source.
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+Welcome to OpenVibe: Source.
new file mode 100644
new file mode 100644
@@ -0,0 +1,32 @@
@@ -0,0 +1,32 @@
+whitelist
+{
+ // Server rules used for sv_pure.
+ //
+ // Do not modify this file.
+ //
+ // See pure_server_whitelist_example.txt for more details on when this file is used and what the rules mean.
+
+ //
+ // Enforce consistency for a few critical files
+ //
+
+ particles/bullet_tracers.pcf trusted_source
+ particles/burningplayer.pcf trusted_source
+ particles/burningplayer_dx80.pcf trusted_source
+ particles/cig_smoke.pcf trusted_source
+ particles/cig_smoke_dx80.pcf trusted_source
+ particles/explosion.pcf trusted_source
+ particles/explosion_dx80.pcf trusted_source
+ particles/explosion_dx90_slow.pcf trusted_source
+ particles/explosion_high.pcf trusted_source
+ particles/disguise.pcf trusted_source
+ particles/flag_particles.pcf trusted_source
+ particles/nemesis.pcf trusted_source
+ particles/player_recent_teleport.pcf trusted_source
+ particles/player_recent_teleport_dx80.pcf trusted_source
+ particles/speechbubbles.pcf trusted_source
+ particles/teleported_fx.pcf trusted_source
+ particles/teleport_status.pcf trusted_source
+ particles/water.pcf trusted_source
+ particles/water_dx80.pcf trusted_source
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
+// OpenVibe: Source default dedicated server config.
+// Individual shards load openvibe_<mode>.cfg after this file.
new file mode 100644
new file mode 100644
@@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
+"trusted_key_list"
+{
+
+ //
+ // This file lists public keys that are trusted by the server.
+ // It should usually only contain official Valve public keys,
+ // so don't modify it.
+ //
+ // See trusted_key_example.txt for how you can add keys issues
+ // by third parties, in order to add their content to the list
+ // of trusted content.
+ //
+
+ // Official Valve content. Key generated 2013/03/01
+ public_key
+ {
+ type "rsa"
+ rsa_public_key "30819D300D06092A864886F70D010101050003818B0030818702818100B1C0F11CB2982F29259507A774D4834377C5B7A38D9A4B3892B598009F16AA109565CB09AD25DE0D3D1A089C3CB68E491921CC142F383383201DE98262A76ED8A6CC78BC51685A0A64A6172C67127AF23E78731F4A82C201D64C9AB80937322184B642727FE142D15CC045F3583E19E3E3E1A9C50C0FC84113573A520A8F7323020111"
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
+// OpenVibe: Source startup script.
+// Intentionally small; mode-specific server config is passed with +exec.
Binary files a/game/openvibe.games/maps/dr_openvibe_dev.bsp and b/game/openvibe.games/maps/dr_openvibe_dev.bsp differ
Binary files a/game/openvibe.games/maps/dr_openvibe_dev.bsp and b/game/openvibe.games/maps/dr_openvibe_dev.bsp differ
Binary files a/game/openvibe.games/maps/fw_openvibe_dev.bsp and b/game/openvibe.games/maps/fw_openvibe_dev.bsp differ
Binary files a/game/openvibe.games/maps/fw_openvibe_dev.bsp and b/game/openvibe.games/maps/fw_openvibe_dev.bsp differ
Binary files a/game/openvibe.games/maps/ov_hub.bsp and b/game/openvibe.games/maps/ov_hub.bsp differ
Binary files a/game/openvibe.games/maps/ov_hub.bsp and b/game/openvibe.games/maps/ov_hub.bsp differ
Binary files a/game/openvibe.games/maps/ph_openvibe_dev.bsp and b/game/openvibe.games/maps/ph_openvibe_dev.bsp differ
Binary files a/game/openvibe.games/maps/ph_openvibe_dev.bsp and b/game/openvibe.games/maps/ph_openvibe_dev.bsp differ
Binary files a/game/openvibe.games/maps/tt_openvibe_dev.bsp and b/game/openvibe.games/maps/tt_openvibe_dev.bsp differ
Binary files a/game/openvibe.games/maps/tt_openvibe_dev.bsp and b/game/openvibe.games/maps/tt_openvibe_dev.bsp differ
new file mode 100644
new file mode 100644
@@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
+"UnlitGeneric"
+{
+ "$basetexture" "vgui/white_additive"
+ "$translucent" "1"
+ "$vertexcolor" "1"
+ "$vertexalpha" "1"
+}
Binary files a/game/openvibe.games/modelsounds.cache and b/game/openvibe.games/modelsounds.cache differ
Binary files a/game/openvibe.games/modelsounds.cache and b/game/openvibe.games/modelsounds.cache differ
new file mode 100644
new file mode 100644
@@ -0,0 +1,1285 @@
@@ -0,0 +1,1285 @@
+///////////////////////////////////////////////////////////
+// Tracker scheme resource file
+//
+// sections:
+// Colors - all the colors used by the scheme
+// BaseSettings - contains settings for app to use to draw controls
+// Fonts - list of all the fonts used by app
+// Borders - description of all the borders
+//
+///////////////////////////////////////////////////////////
+Scheme
+{
+ //////////////////////// COLORS ///////////////////////////
+ // color details
+ // this is a list of all the colors used by the scheme
+ Colors
+ {
+ // base colors
+ "Orange" "255 176 0 255"
+ "OrangeDim" "255 176 0 120"
+ "LightOrange" "188 112 0 128"
+
+ "Red" "192 28 0 140"
+ "Black" "0 0 0 196"
+ "TransparentBlack" "0 0 0 196"
+ "TransparentLightBlack" "0 0 0 90"
+
+ "Blank" "0 0 0 0"
+ "ForTesting" "255 0 0 32"
+ "ForTesting_Magenta" "255 0 255 255"
+ "ForTesting_MagentaDim" "255 0 255 120"
+ }
+
+ ///////////////////// BASE SETTINGS ////////////////////////
+ //
+ // default settings for all panels
+ // controls use these to determine their settings
+ BaseSettings
+ {
+ "FgColor" "255 220 0 100"
+ "FgColor_vrmode" "255 220 0 200"
+ "BgColor" "0 0 0 76"
+
+ "Panel.FgColor" "255 220 0 100"
+ "Panel.BgColor" "0 0 0 76"
+
+ "BrightFg" "255 220 0 255"
+
+ "DamagedBg" "180 0 0 200"
+ "DamagedFg" "180 0 0 230"
+ "BrightDamagedFg" "255 0 0 255"
+
+ // weapon selection colors
+ "SelectionNumberFg" "255 220 0 255"
+ "SelectionTextFg" "255 220 0 255"
+ "SelectionEmptyBoxBg" "0 0 0 80"
+ "SelectionBoxBg" "0 0 0 80"
+ "SelectionSelectedBoxBg" "0 0 0 80"
+
+ "ZoomReticleColor" "255 220 0 255"
+
+ // HL1-style HUD colors
+ "Yellowish" "255 160 0 255"
+ "Normal" "255 208 64 255"
+ "Caution" "255 48 0 255"
+
+ // Top-left corner of the "Half-Life 2" on the main screen
+ "Main.Title1.X" "76"
+ "Main.Title1.Y" "184"
+ "Main.Title1.Color" "255 255 255 255"
+
+ // Top-left corner of the "DEATHMATCH" on the main screen
+ "Main.Title2.X" "315"
+ "Main.Title2.Y" "222"
+ "Main.Title2.Color" "255 255 255 180"
+
+ // Top-left corner of the menu on the main screen
+ "Main.Menu.X" "76"
+ "Main.Menu.Y" "240"
+
+ // Blank space to leave beneath the menu on the main screen
+ "Main.BottomBorder" "32"
+
+ // Deck colors
+ "SteamDeckLoadingBar" "250 128 20 255"
+ "SteamDeckSpinner" "201 100 0 255"
+ "SteamDeckLoadingText" "181 179 175 255"
+
+
+///HERE
+ // vgui_controls color specifications
+ Border.Bright "LightOrange" // the lit side of a control
+ Border.Dark "LightOrange" // the dark/unlit side of a control
+ Border.Selection "Blank" // the additional border color for displaying the default/selected button
+
+ Button.TextColor "Orange"
+ Button.BgColor "Blank"
+ Button.ArmedTextColor "Orange"
+ Button.ArmedBgColor "Red"
+ Button.DepressedTextColor "Orange"
+ Button.DepressedBgColor "Red"
+
+ CheckButton.TextColor "Orange"
+ CheckButton.SelectedTextColor "Orange"
+ CheckButton.BgColor "TransparentBlack"
+ CheckButton.Border1 "Border.Dark" // the left checkbutton border
+ CheckButton.Border2 "Border.Bright" // the right checkbutton border
+ CheckButton.Check "Orange" // color of the check itself
+
+ ComboBoxButton.ArrowColor "Orange"
+ ComboBoxButton.ArmedArrowColor "Orange"
+ ComboBoxButton.BgColor "TransparentBlack"
+ ComboBoxButton.DisabledBgColor "Blank"
+
+ Frame.BgColor "TransparentBlack"
+ Frame.OutOfFocusBgColor "TransparentBlack"
+ Frame.FocusTransitionEffectTime "0.0" // time it takes for a window to fade in/out on focus/out of focus
+ Frame.TransitionEffectTime "0.0" // time it takes for a window to fade in/out on open/close
+ Frame.AutoSnapRange "0"
+ FrameGrip.Color1 "Blank"
+ FrameGrip.Color2 "Blank"
+ FrameTitleButton.FgColor "Blank"
+ FrameTitleButton.BgColor "Blank"
+ FrameTitleButton.DisabledFgColor "Blank"
+ FrameTitleButton.DisabledBgColor "Blank"
+ FrameSystemButton.FgColor "Blank"
+ FrameSystemButton.BgColor "Blank"
+ FrameSystemButton.Icon ""
+ FrameSystemButton.DisabledIcon ""
+ FrameTitleBar.TextColor "Orange"
+ FrameTitleBar.BgColor "Blank"
+ FrameTitleBar.DisabledTextColor "Orange"
+ FrameTitleBar.DisabledBgColor "Blank"
+
+ GraphPanel.FgColor "Orange"
+ GraphPanel.BgColor "TransparentBlack"
+
+ Label.TextDullColor "Orange"
+ Label.TextColor "Orange"
+ Label.TextBrightColor "Orange"
+ Label.SelectedTextColor "Orange"
+ Label.BgColor "Blank"
+ Label.DisabledFgColor1 "Blank"
+ Label.DisabledFgColor2 "LightOrange"
+
+ ListPanel.TextColor "Orange"
+ ListPanel.BgColor "TransparentBlack"
+ ListPanel.SelectedTextColor "Black"
+ ListPanel.SelectedBgColor "Red"
+ ListPanel.SelectedOutOfFocusBgColor "Red"
+ ListPanel.EmptyListInfoTextColor "Orange"
+
+ Menu.TextColor "Orange"
+ Menu.BgColor "TransparentBlack"
+ Menu.ArmedTextColor "Orange"
+ Menu.ArmedBgColor "Red"
+ Menu.TextInset "6"
+
+ Chat.TypingText "Orange"
+
+ Panel.FgColor "OrangeDim"
+ Panel.BgColor "blank"
+
+ ProgressBar.FgColor "Orange"
+ ProgressBar.BgColor "TransparentBlack"
+
+ PropertySheet.TextColor "Orange"
+ PropertySheet.SelectedTextColor "Orange"
+ PropertySheet.TransitionEffectTime "0.25" // time to change from one tab to another
+
+ RadioButton.TextColor "Orange"
+ RadioButton.SelectedTextColor "Orange"
+
+ RichText.TextColor "Orange"
+ RichText.BgColor "Blank"
+ RichText.SelectedTextColor "Orange"
+ RichText.SelectedBgColor "Blank"
+
+ ScrollBarButton.FgColor "Orange"
+ ScrollBarButton.BgColor "Blank"
+ ScrollBarButton.ArmedFgColor "Orange"
+ ScrollBarButton.ArmedBgColor "Blank"
+ ScrollBarButton.DepressedFgColor "Orange"
+ ScrollBarButton.DepressedBgColor "Blank"
+
+ ScrollBarSlider.FgColor "Blank" // nob color
+ ScrollBarSlider.BgColor "Blank" // slider background color
+
+ SectionedListPanel.HeaderTextColor "Orange"
+ SectionedListPanel.HeaderBgColor "Blank"
+ SectionedListPanel.DividerColor "Black"
+ SectionedListPanel.TextColor "Orange"
+ SectionedListPanel.BrightTextColor "Orange"
+ SectionedListPanel.BgColor "TransparentLightBlack"
+ SectionedListPanel.SelectedTextColor "Black"
+ SectionedListPanel.SelectedBgColor "Red"
+ SectionedListPanel.OutOfFocusSelectedTextColor "Black"
+ SectionedListPanel.OutOfFocusSelectedBgColor "255 255 255 32"
+
+ Slider.NobColor "108 108 108 255"
+ Slider.TextColor "127 140 127 255"
+ Slider.TrackColor "31 31 31 255"
+ Slider.DisabledTextColor1 "117 117 117 255"
+ Slider.DisabledTextColor2 "30 30 30 255"
+
+ TextEntry.TextColor "Orange"
+ TextEntry.BgColor "TransparentBlack"
+ TextEntry.CursorColor "Orange"
+ TextEntry.DisabledTextColor "Orange"
+ TextEntry.DisabledBgColor "Blank"
+ TextEntry.SelectedTextColor "Black"
+ TextEntry.SelectedBgColor "Red"
+ TextEntry.OutOfFocusSelectedBgColor "Red"
+ TextEntry.FocusEdgeColor "TransparentBlack"
+
+ ToggleButton.SelectedTextColor "Orange"
+
+ Tooltip.TextColor "TransparentBlack"
+ Tooltip.BgColor "Red"
+
+ TreeView.BgColor "TransparentBlack"
+
+ WizardSubPanel.BgColor "Blank"
+
+ // scheme-specific colors
+ "FgColor" "Orange"
+ "BgColor" "TransparentBlack"
+
+ "ViewportBG" "Blank"
+ "team0" "204 204 204 255" // Spectators
+ "team1" "255 64 64 255" // CT's
+ "team2" "153 204 255 255" // T's
+
+ "MapDescriptionText" "Orange" // the text used in the map description window
+ "CT_Blue" "153 204 255 255"
+ "T_Red" "255 64 64 255"
+ "Hostage_Yellow" "Panel.FgColor"
+ "HudIcon_Green" "0 160 0 255"
+ "HudIcon_Red" "160 0 0 255"
+
+ // CHudMenu
+ "ItemColor" "255 167 42 200" // default 255 167 42 255
+ "MenuColor" "233 208 173 255"
+ "MenuBoxBg" "0 0 0 100"
+
+ // weapon selection colors
+ "SelectionNumberFg" "255 220 0 200"
+ "SelectionTextFg" "255 220 0 200"
+ "SelectionEmptyBoxBg" "0 0 0 80"
+ "SelectionBoxBg" "0 0 0 80"
+ "SelectionSelectedBoxBg" "0 0 0 190"
+
+ // Hint message colors
+ "HintMessageFg" "255 255 255 255"
+ "HintMessageBg" "0 0 0 60"
+
+ "ProgressBarFg" "255 30 13 255"
+ }
+
+ //////////////////////// BITMAP FONT FILES /////////////////////////////
+ //
+ // Bitmap Fonts are ****VERY*** expensive static memory resources so they are purposely sparse
+ BitmapFontFiles
+ {
+ // UI buttons, custom font, (256x64)
+ "Buttons" "materials/vgui/fonts/buttons_32.vbf"
+ }
+
+
+ //////////////////////// FONTS /////////////////////////////
+ //
+ // describes all the fonts
+ Fonts
+ {
+ // fonts are used in order that they are listed
+ // fonts are used in order that they are listed
+ "DebugFixed"
+ {
+ "1"
+ {
+ "name" "Courier New"
+ "tall" "14"
+ "weight" "400"
+ "antialias" "1"
+ }
+ }
+ // fonts are used in order that they are listed
+ "DebugFixedSmall"
+ {
+ "1"
+ {
+ "name" "Courier New"
+ "tall" "14"
+ "weight" "400"
+ "antialias" "1"
+ }
+ }
+ // fonts listed later in the order will only be used if they fulfill a range not already filled
+ // if a font fails to load then the subsequent fonts will replace
+ Default
+ {
+ "1"
+ {
+ "name" "GorDIN"
+ "tall" "16"
+ "tall" "12"
+ "weight" "600"
+ "antialias" "1"
+ "yres" "1 599"
+ }
+ "2"
+ {
+ "name" "GorDIN"
+ "tall" "22"
+ "tall" "14"
+ "weight" "600"
+ "antialias" "1"
+ "yres" "600 767"
+ }
+ "3"
+ {
+ "name" "GorDIN"
+ "tall" "26"
+ "tall" "19"
+ "weight" "700"
+ "antialias" "1"
+ "yres" "768 1023"
+ }
+ "4"
+ {
+ "name" "GorDIN"
+ "tall" "30"
+ "tall" "21"
+ "weight" "700"
+ "antialias" "1"
+ "yres" "1024 1199"
+ }
+ "5" // Proportional - misyl
+ {
+ "name" "GorDIN"
+ "tall" "14"
+ "tall" "10"
+ "weight" "700"
+ "antialias" "1"
+ "additive" "1"
+ }
+ }
+ "DefaultSmall"
+ {
+ "1"
+ {
+ "name" "GorDIN"
+ "tall" "12"
+ "weight" "400"
+ "yres" "480 599"
+ "antialias" "1"
+ }
+ "2"
+ {
+ "name" "GorDIN"
+ "tall" "14"
+ "weight" "400"
+ "yres" "600 767"
+ "antialias" "1"
+ }
+ "3"
+ {
+ "name" "GorDIN"
+ "tall" "18"
+ "weight" "400"
+ "yres" "768 1023"
+ "antialias" "1"
+ }
+ "4"
+ {
+ "name" "GorDIN"
+ "tall" "20"
+ "weight" "400"
+ "yres" "1024 1199"
+ "antialias" "1"
+ }
+ "5" // Proportional - misyl
+ {
+ "name" "GorDIN"
+ "tall" "12"
+ "weight" "0"
+ "antialias" "1"
+ }
+ }
+ "DefaultVerySmall"
+ {
+ "1"
+ {
+ "name" "GorDIN"
+ "tall" "14"
+ "weight" "0"
+ "yres" "480 599"
+ "antialias" "1"
+ }
+ "2"
+ {
+ "name" "GorDIN"
+ "tall" "15"
+ "weight" "0"
+ "yres" "600 767"
+ "antialias" "1"
+ }
+ "3"
+ {
+ "name" "GorDIN"
+ "tall" "16"
+ "weight" "0"
+ "yres" "768 1023"
+ "antialias" "1"
+ }
+ "4"
+ {
+ "name" "GorDIN"
+ "tall" "20"
+ "weight" "0"
+ "yres" "1024 1199"
+ "antialias" "1"
+ }
+ "5" // Proportional - misyl
+ {
+ "name" "GorDIN"
+ "tall" "12"
+ "weight" "0"
+ "antialias" "1"
+ }
+ }
+ WeaponIcons
+ {
+ "1"
+ {
+ "name" "HalfLife2"
+ "tall" "70"
+ "tall" "64"
+ "tall_hidef" "58"
+ "weight" "0"
+ "antialias" "1"
+ "additive" "1"
+ "custom" "1"
+ }
+ }
+ WeaponIconsSelected
+ {
+ "1"
+ {
+ "name" "HalfLife2"
+ "tall" "70"
+ "tall" "64"
+ "tall_hidef" "58"
+ "weight" "0"
+ "antialias" "1"
+ "blur" "5"
+ "scanlines" "2"
+ "additive" "1"
+ "custom" "1"
+ }
+ }
+ WeaponIconsSmall
+ {
+ "1"
+ {
+ "name" "HalfLife2"
+ "tall" "36"
+ "tall" "32"
+ "weight" "0"
+ "antialias" "1"
+ "additive" "1"
+ "custom" "1"
+ }
+ }
+ WeaponIconsSmall_ABitSmaller
+ {
+ "1"
+ {
+ "name" "HalfLife2"
+ "tall" "34"
+ "tall" "27"
+ "weight" "0"
+ "antialias" "1"
+ "additive" "1"
+ "custom" "1"
+ }
+ }
+ WeaponIconsSmallSecondary
+ {
+ "1"
+ {
+ "name" "HalfLife2"
+ "tall" "34"
+ "tall" "25"
+ "weight" "0"
+ "antialias" "1"
+ "additive" "1"
+ "custom" "1"
+ }
+ }
+ FlashlightDeck
+ {
+ "1"
+ {
+ "name" "HalfLife2"
+ "tall" "46"
+ "weight" "0"
+ "antialias" "1"
+ "additive" "1"
+ "custom" "1"
+ }
+ }
+ Crosshairs
+ {
+ "1"
+ {
+ "name" "HalfLife2"
+ "tall" "40"
+ "tall" "41"
+ "weight" "0"
+ "antialias" "0"
+ "additive" "1"
+ "custom" "1"
+ "yres" "1 1599"
+ "yres" "1 1439"
+ }
+ "2"
+ {
+ "name" "HalfLife2"
+ "tall" "80"
+ "weight" "0"
+ "antialias" "1"
+ "additive" "1"
+ "custom" "1"
+ "yres" "1600 3199"
+ "yres" "1440 3199"
+ }
+ "3"
+ {
+ "name" "HalfLife2"
+ "tall" "120"
+ "weight" "0"
+ "antialias" "1"
+ "additive" "1"
+ "custom" "1"
+ "yres" "3200 4799"
+ }
+ "4"
+ {
+ "name" "HalfLife2"
+ "tall" "17"
+ "weight" "0"
+ "antialias" "1"
+ "additive" "1"
+ "custom" "1"
+ }
+ }
+ QuickInfo
+ {
+ "1"
+ {
+ "name" "HL2cross"
+ "tall" "36"
+ "tall" "28"
+ "tall" "50"
+ "weight" "0"
+ "antialias" "1"
+ "additive" "1"
+ "custom" "1"
+ }
+ }
+ HudNumbers
+ {
+ "1"
+ {
+ "name" "HalfLife2"
+ "tall" "32"
+ "tall" "40"
+ "weight" "0"
+ "antialias" "1"
+ "additive" "1"
+ "custom" "1"
+ }
+ }
+ HudNumbersGlow
+ {
+ "1"
+ {
+ "name" "HalfLife2"
+ "tall" "32"
+ "tall" "40"
+ "weight" "0"
+ "blur" "4"
+ "scanlines" "2"
+ "antialias" "1"
+ "additive" "1"
+ "custom" "1"
+ }
+ }
+ HudNumbersSmall
+ {
+ "1"
+ {
+ "name" "HalfLife2"
+ "name" "Helvetica Bold"
+ "tall" "16"
+ "tall" "26"
+ "weight" "1000"
+ "additive" "1"
+ "antialias" "1"
+ "custom" "1"
+ }
+ }
+ HudSelectionNumbers
+ {
+ "1"
+ {
+ "name" "GorDIN"
+ "tall" "16"
+ "tall" "11"
+ "weight" "700"
+ "antialias" "1"
+ "additive" "1"
+ }
+ }
+ HudHintTextLarge
+ {
+ "1"
+ {
+ "name" "GorDIN"
+ "tall" "22"
+ "tall" "15"
+ "weight" "700"
+ "antialias" "1"
+ "additive" "1"
+ }
+ }
+ HudHintTextSmall
+ {
+ "1"
+ {
+ "name" "GorDIN"
+ "tall" "18"
+ "tall" "13"
+ "weight" "0"
+ "antialias" "1"
+ "additive" "1"
+ }
+ }
+ HudSelectionText
+ {
+ "1"
+ {
+ "name" "GorDIN"
+ "tall" "12"
+ "tall" "12"
+ "weight" "600"
+ "antialias" "1"
+ "yres" "1 599"
+ "additive" "1"
+ }
+ "2"
+ {
+ "name" "GorDIN"
+ "tall" "14"
+ "tall" "12"
+ "weight" "600"
+ "antialias" "1"
+ "yres" "600 767"
+ "additive" "1"
+ }
+ "3"
+ {
+ "name" "GorDIN"
+ "tall" "18"
+ "tall" "14"
+ "weight" "600"
+ "antialias" "1"
+ "yres" "768 1023"
+ "additive" "1"
+ }
+ "4"
+ {
+ "name" "GorDIN"
+ "tall" "22"
+ "tall" "17"
+ "weight" "600"
+ "antialias" "1"
+ "yres" "1024 1199"
+ "additive" "1"
+ }
+ "5"
+ {
+ "name" "GorDIN"
+ "tall" "9"
+ "tall" "7"
+ "weight" "700"
+ "antialias" "1"
+ "additive" "1"
+ }
+ }
+ GameUIButtons
+ {
+ "1"
+ {
+ "bitmap" "1"
+ "name" "Buttons"
+ "scalex" "0.63"
+ "scaley" "0.63"
+ "scalex_hidef" "1.0"
+ "scaley_hidef" "1.0"
+ }
+ }
+ BudgetLabel
+ {
+ "1"
+ {
+ "name" "Courier New"
+ "tall" "14"
+ "weight" "400"
+ "outline" "1"
+ }
+ }
+ DebugOverlay
+ {
+ "1"
+ {
+ "name" "Courier New"
+ "tall" "14"
+ "weight" "400"
+ "outline" "1"
+ }
+ "1"
+ {
+ "name" "Tahoma"
+ "tall" "18"
+ "weight" "200"
+ "outline" "1"
+ }
+ }
+ "CloseCaption_Normal"
+ {
+ "1"
+ {
+ "name" "GorDIN"
+ "tall" "15"
+ "tall" "12"
+ "weight" "600" // todo(misyl): do we want this at 500 or 600?
+ "antialias" "1"
+ }
+ }
+ "CloseCaption_Italic"
+ {
+ "1"
+ {
+ "name" "GorDIN"
+ "tall" "15"
+ "tall" "12"
+ "weight" "600"
+ "italic" "1"
+ "antialias" "1"
+ }
+ }
+ "CloseCaption_Bold"
+ {
+ "1"
+ {
+ "name" "GorDIN"
+ "tall" "15"
+ "tall" "12"
+ "weight" "700"
+ "antialias" "1"
+ }
+ }
+ "CloseCaption_BoldItalic"
+ {
+ "1"
+ {
+ "name" "GorDIN"
+ "tall" "15"
+ "tall" "12"
+ "weight" "700"
+ "italic" "1"
+ "antialias" "1"
+ }
+ }
+ "CloseCaption_Small"
+ {
+ "1"
+ {
+ "name" "GorDIN"
+ "tall" "15"
+ "tall" "12"
+ "weight" "600"
+ "antialias" "1"
+ }
+ }
+ // this is the symbol font
+ "Marlett"
+ {
+ "1"
+ {
+ "name" "Marlett"
+ "tall" "14"
+ "weight" "0"
+ "symbol" "1"
+ }
+ }
+ "Trebuchet24"
+ {
+ "1"
+ {
+ "name" "GorDIN"
+ "tall" "24"
+ "weight" "700"
+ "antialias" "1"
+ "additive" "1"
+ }
+ }
+ "Trebuchet18"
+ {
+ "1"
+ {
+ "name" "GorDIN"
+ "tall" "18"
+ "weight" "700"
+ "antialias" "1"
+ }
+ }
+ ClientTitleFont
+ {
+ "1"
+ {
+ "name" "HL2MP"
+ "tall" "46"
+ "weight" "0"
+ "additive" "0"
+ "antialias" "1"
+ "custom" "1"
+ }
+ }
+
+ "ChatFont"
+ {
+ "1"
+ {
+ "name" "Verdana"
+ "tall" "12"
+ "weight" "700"
+ "yres" "480 599"
+ "dropshadow" "1"
+ }
+ "2"
+ {
+ "name" "Verdana"
+ "tall" "13"
+ "weight" "700"
+ "yres" "600 767"
+ "dropshadow" "1"
+ }
+ "3"
+ {
+ "name" "Verdana"
+ "tall" "14"
+ "weight" "700"
+ "yres" "768 1023"
+ "dropshadow" "1"
+ }
+ "4"
+ {
+ "name" "Verdana"
+ "tall" "20"
+ "weight" "700"
+ "yres" "1024 1199"
+ "dropshadow" "1"
+ }
+ "5" // Misyl: Proportional
+ {
+ "name" "Verdana"
+ "tall" "9"
+ "weight" "700"
+ "antialias" "1"
+ }
+ }
+
+ "TargetID"
+ {
+ "1"
+ {
+ "name" "GorDIN"
+ "tall" "24"
+ "weight" "0"
+ "antialias" "1"
+ "additive" "1"
+ }
+ }
+
+ "HudNumbersTimer"
+ {
+ "1"
+ {
+ "name" "GorDIN"
+ "tall" "15"
+ "weight" "0"
+ "antialias" "1"
+ "additive" "1"
+ }
+ }
+
+ "HL2MPTypeDeath"
+ {
+ "1"
+ {
+ "name" "HL2MP" // csd.ttf
+ "tall" "32"
+ "weight" "0"
+ "additive" "1"
+ "antialias" "1"
+ "custom" "1"
+ }
+ }
+
+ // Used by scoreboard and spectator UI for names which don't map in the normal fashion
+ "DefaultVerySmallFallBack"
+ {
+ "1"
+ {
+ "name" "GorDIN"
+ "tall" "10"
+ "weight" "0"
+ "range" "0x0000 0x017F" // Basic Latin, Latin-1 Supplement, Latin Extended-A
+ "yres" "480 599"
+ "antialias" "1"
+ }
+ "2"
+ {
+ "name" "GorDIN"
+ "tall" "12"
+ "weight" "0"
+ "range" "0x0000 0x017F" // Basic Latin, Latin-1 Supplement, Latin Extended-A
+ "yres" "600 1199"
+ "antialias" "1"
+ }
+ "3"
+ {
+ "name" "GorDIN"
+ "tall" "15"
+ "weight" "0"
+ "range" "0x0000 0x017F" // Basic Latin, Latin-1 Supplement, Latin Extended-A
+ "yres" "1200 6000"
+ "antialias" "1"
+ }
+ "4" // Proportional - misyl
+ {
+ "name" "GorDIN"
+ "tall" "10"
+ "weight" "0"
+ "antialias" "1"
+ }
+ }
+
+ CreditsLogo
+ {
+ "1"
+ {
+ "name" "HalfLife2"
+ "tall" "34"
+ "weight" "0"
+ "antialias" "1"
+ "additive" "1"
+ "custom" "1"
+ }
+ }
+ CreditsIcons
+ {
+ "1"
+ {
+ "name" "HalfLife2"
+ "tall" "34"
+ "weight" "0"
+ "antialias" "1"
+ "additive" "1"
+ "custom" "1"
+ }
+ }
+ CreditsText
+ {
+ "1"
+ {
+ "name" "Trebuchet MS"
+ "tall" "20"
+ "weight" "900"
+ "antialias" "1"
+ "additive" "1"
+ "yres" "480 899"
+ }
+ "2"
+ {
+ "name" "Trebuchet MS"
+ "tall" "12"
+ "weight" "900"
+ "antialias" "1"
+ "additive" "1"
+ }
+ }
+ CreditsOutroLogos
+ {
+ "1"
+ {
+ "name" "HalfLife2"
+ "tall" "34"
+ "weight" "0"
+ "antialias" "1"
+ "additive" "1"
+ "custom" "1"
+ }
+ }
+ CreditsOutroValve
+ {
+ "1"
+ {
+ "name" "HalfLife2"
+ "tall" "48"
+ "weight" "0"
+ "antialias" "1"
+ "additive" "1"
+ "custom" "1"
+ }
+ }
+ CreditsOutroText
+ {
+ "1"
+ {
+ "name" "Verdana"
+ "name" "Courier Bold"
+ "tall" "16"
+ "weight" "900"
+ "antialias" "1"
+ }
+ }
+ CenterPrintText
+ {
+ // note that this scales with the screen resolution
+ "1"
+ {
+ "name" "GorDIN"
+ "tall" "18"
+ "weight" "700"
+ "antialias" "1"
+ "additive" "1"
+ }
+ }
+ HDRDemoText
+ {
+ // note that this scales with the screen resolution
+ "1"
+ {
+ "name" "Trebuchet MS"
+ "tall" "24"
+ "weight" "900"
+ "antialias" "1"
+ "additive" "1"
+ }
+ }
+ "AchievementNotification"
+ {
+ "1"
+ {
+ "name" "GorDIN"
+ "tall" "14"
+ "weight" "900"
+ "antialias" "1"
+ }
+ }
+ "CommentaryDefault"
+ {
+ "1"
+ {
+ "name" "GorDIN"
+ "tall" "12"
+ "weight" "700"
+ "yres" "480 599"
+ "antialias" "1"
+ }
+ "2"
+ {
+ "name" "GorDIN"
+ "tall" "13"
+ "tall" "20"
+ "weight" "700"
+ "yres" "600 767"
+ "antialias" "1"
+ }
+ "3"
+ {
+ "name" "GorDIN"
+ "tall" "14"
+ "weight" "700"
+ "yres" "768 1023"
+ "antialias" "1"
+ }
+ "4"
+ {
+ "name" "GorDIN"
+ "tall" "20"
+ "weight" "700"
+ "yres" "1024 1199"
+ "antialias" "1"
+ }
+ "6" // Proportional
+ {
+ "name" "GorDIN"
+ "tall" "10"
+ "weight" "700"
+ "antialias" "1"
+ }
+
+ }
+ "SteamDeckLoadingText"
+ {
+ "7"
+ {
+ "name" "GorDIN"
+ "tall" "24"
+ "weight" "700"
+ "antialias" "1"
+ }
+ }
+
+ "PDump"
+ {
+ "1"
+ {
+ "name" "GorDIN"
+ "tall" "24"
+ "weight" "400"
+ "yres" "480 599"
+ "antialias" "1"
+ }
+ "2"
+ {
+ "name" "GorDIN"
+ "tall" "24"
+ "weight" "400"
+ "yres" "600 767"
+ "antialias" "1"
+ }
+ "3"
+ {
+ "name" "GorDIN"
+ "tall" "24"
+ "weight" "400"
+ "yres" "768 1023"
+ "antialias" "1"
+ }
+ "4"
+ {
+ "name" "GorDIN"
+ "tall" "24"
+ "weight" "400"
+ "yres" "1024 1199"
+ "antialias" "1"
+ }
+ "5"
+ {
+ "name" "GorDIN"
+ "tall" "40"
+ "weight" "400"
+ "yres" "1200 10000"
+ "antialias" "1"
+ }
+ }
+ "PDumpSmall"
+ {
+ "1"
+ {
+ "name" "GorDIN"
+ "tall" "12"
+ "weight" "400"
+ "yres" "480 599"
+ "antialias" "1"
+ }
+ "2"
+ {
+ "name" "GorDIN"
+ "tall" "14"
+ "weight" "400"
+ "yres" "600 767"
+ "antialias" "1"
+ }
+ "3"
+ {
+ "name" "GorDIN"
+ "tall" "18"
+ "weight" "400"
+ "yres" "768 1023"
+ "antialias" "1"
+ }
+ "4"
+ {
+ "name" "GorDIN"
+ "tall" "20"
+ "weight" "400"
+ "yres" "1024 1199"
+ "antialias" "1"
+ }
+ "5"
+ {
+ "name" "GorDIN"
+ "tall" "35"
+ "weight" "400"
+ "yres" "1200 10000"
+ "antialias" "1"
+ }
+ }
+ "PDumpVerySmall"
+ {
+ "1"
+ {
+ "name" "GorDIN"
+ "tall" "14"
+ "weight" "400"
+ "yres" "480 599"
+ "antialias" "1"
+ }
+ "2"
+ {
+ "name" "GorDIN"
+ "tall" "15"
+ "weight" "400"
+ "yres" "600 767"
+ "antialias" "1"
+ }
+ "3"
+ {
+ "name" "GorDIN"
+ "tall" "16"
+ "weight" "400"
+ "yres" "768 1023"
+ "antialias" "1"
+ }
+ "4"
+ {
+ "name" "GorDIN"
+ "tall" "20"
+ "weight" "400"
+ "yres" "1024 1199"
+ "antialias" "1"
+ }
+ "5"
+ {
+ "name" "GorDIN"
+ "tall" "30"
+ "weight" "400"
+ "yres" "1200 10000"
+ "antialias" "1"
+ }
+ }
+ }
+
+
+ //////////////////////// CUSTOM FONT FILES /////////////////////////////
+ //
+ // specifies all the custom (non-system) font files that need to be loaded to service the above described fonts
+ CustomFontFiles
+ {
+ "1" "resource/HALFLIFE2.ttf"
+ "2" "resource/HL2crosshairs.ttf"
+ "3" "resource/HL2MP.ttf"
+ "4" "resource/linux_fonts/DejaVuSans.ttf"
+ "5" "resource/linux_fonts/DejaVuSans-Bold.ttf"
+ "6" "resource/linux_fonts/DejaVuSans-BoldOblique.ttf"
+ "7" "resource/linux_fonts/DejaVuSans-Oblique.ttf"
+ "8" "resource/linux_fonts/LiberationSans-Regular.ttf"
+ "9" "resource/linux_fonts/LiberationSans-Bold.ttf"
+ "10" "resource/linux_fonts/LiberationMono-Regular.ttf"
+
+ "11" "resource/GorDIN-Regular.ttf"
+ "12" "resource/GorDIN-Bold.ttf"
+ "13" "resource/GorDIN-Semibold.ttf"
+ "14" "resource/GorDIN-Black.ttf"
+ "15" "resource/GorDIN-Light.ttf"
+ }
+
+}
@@ -2,38 +2,38 @@
@@ -2,38 +2,38 @@
{
{
"1"
"1"
{
{
- "label" "OpenVibe Hub"
- "command" "engine openvibe_menu"
+ "label" "OpenVibe.Games"
+ "command" "engine echo OpenVibe.Games"
"OnlyInGame" "0"
"OnlyInGame" "0"
}
}
"2"
"2"
{
{
"label" "Enter Hub"
"label" "Enter Hub"
- "command" "engine ov_join hub"
+ "command" "engine connect 127.0.0.1:27015"
"OnlyInGame" "0"
"OnlyInGame" "0"
}
}
"3"
"3"
{
{
"label" "Prop Hunt"
"label" "Prop Hunt"
- "command" "engine ov_join prophunt"
+ "command" "engine connect 127.0.0.2:27016"
"OnlyInGame" "0"
"OnlyInGame" "0"
}
}
"4"
"4"
{
{
"label" "Deathrun"
"label" "Deathrun"
- "command" "engine ov_join deathrun"
+ "command" "engine connect 127.0.0.3:27017"
"OnlyInGame" "0"
"OnlyInGame" "0"
}
}
"5"
"5"
{
{
"label" "Fort Wars"
"label" "Fort Wars"
- "command" "engine ov_join fortwars"
+ "command" "engine connect 127.0.0.4:27018"
"OnlyInGame" "0"
"OnlyInGame" "0"
}
}
"6"
"6"
{
{
"label" "Traitor Town"
"label" "Traitor Town"
- "command" "engine ov_join traitortown"
+ "command" "engine connect 127.0.0.5:27019"
"OnlyInGame" "0"
"OnlyInGame" "0"
}
}
"7"
"7"
new file mode 100644
new file mode 100644
Binary files /dev/null and b/game/openvibe.games/resource/gordin-black.ttf differ
Binary files /dev/null and b/game/openvibe.games/resource/gordin-black.ttf differ
new file mode 100644
new file mode 100644
Binary files /dev/null and b/game/openvibe.games/resource/gordin-bold.ttf differ
Binary files /dev/null and b/game/openvibe.games/resource/gordin-bold.ttf differ
new file mode 100644
new file mode 100644
Binary files /dev/null and b/game/openvibe.games/resource/gordin-light.ttf differ
Binary files /dev/null and b/game/openvibe.games/resource/gordin-light.ttf differ
new file mode 100644
new file mode 100644
Binary files /dev/null and b/game/openvibe.games/resource/gordin-regular.ttf differ
Binary files /dev/null and b/game/openvibe.games/resource/gordin-regular.ttf differ
new file mode 100644
new file mode 100644
Binary files /dev/null and b/game/openvibe.games/resource/gordin-semibold.ttf differ
Binary files /dev/null and b/game/openvibe.games/resource/gordin-semibold.ttf differ
new file mode 100644
new file mode 100644
@@ -0,0 +1,90 @@
@@ -0,0 +1,90 @@
+"lang"
+{
+ "Language" "English"
+ "Tokens"
+ {
+ // Game title
+ "OPENVIBE_GAME_TITLE" "OpenVibe: Source"
+ "OPENVIBE_DOMAIN" "openvibe.games"
+
+ // Hub
+ "OV_HUB_WELCOME" "Welcome to OpenVibe Hub! Step on a portal pad to join a game."
+ "OV_HUB_PORTAL_PROPHUNT" "Prop Hunt Portal"
+ "OV_HUB_PORTAL_DEATHRUN" "Deathrun Portal"
+ "OV_HUB_PORTAL_FORTWARS" "Fort Wars Portal"
+ "OV_HUB_PORTAL_TRAITORTOWN" "Traitor Town Portal"
+ "OV_HUB_SHOP_NPC" "Models Shop"
+ "OV_HUB_TRAILS_NPC" "Trails Shop"
+ "OV_HUB_INVENTORY" "Your Inventory"
+
+ // Shop
+ "OV_SHOP_TITLE" "OpenVibe Shop"
+ "OV_SHOP_BALANCE" "Balance: %coins% VibeCoins"
+ "OV_SHOP_BUY" "Buy"
+ "OV_SHOP_EQUIP" "Equip"
+ "OV_SHOP_OWNED" "Owned"
+ "OV_SHOP_INSUFFICIENT" "Not enough VibeCoins!"
+ "OV_SHOP_PURCHASED" "Purchased: %item%"
+ "OV_SHOP_EQUIPPED" "Now using: %item%"
+
+ // Currency
+ "OV_CURRENCY_NAME" "VibeCoins"
+ "OV_CURRENCY_EARNED" "+%amount% VibeCoins earned!"
+
+ // Round phases
+ "OV_PHASE_WAITING" "Waiting for players…"
+ "OV_PHASE_STARTING" "Round starting in %seconds%s…"
+ "OV_PHASE_ACTIVE" "%seconds%s remaining"
+ "OV_PHASE_ENDED" "Round over!"
+ "OV_MIN_PLAYERS" "Need %min% players to start (%current% connected)"
+
+ // Prop Hunt
+ "OV_PH_TITLE" "Prop Hunt"
+ "OV_PH_ROLE_HUNTER" "You are a HUNTER — find and eliminate the props!"
+ "OV_PH_ROLE_PROP" "You are a PROP — hide before the hunters are released!"
+ "OV_PH_HIDE_PHASE" "Props are hiding — %seconds%s"
+ "OV_PH_HUNT_PHASE" "Hunt has begun! %props% props remaining — %seconds%s"
+ "OV_PH_WIN_HUNTERS" "Hunters win! All props eliminated."
+ "OV_PH_WIN_PROPS" "Props win! Survived the hunt."
+
+ // Deathrun
+ "OV_DR_TITLE" "Deathrun"
+ "OV_DR_ROLE_RUNNER" "You are a RUNNER — reach the end!"
+ "OV_DR_ROLE_ACTIVATOR" "You are the ACTIVATOR — stop the runners!"
+ "OV_DR_WIN_RUNNERS" "Runners win! %finished% made it to the end."
+ "OV_DR_WIN_ACTIVATOR" "Activator wins! All runners eliminated."
+
+ // Fort Wars
+ "OV_FW_TITLE" "Fort Wars"
+ "OV_FW_TEAM_A" "Red Team"
+ "OV_FW_TEAM_B" "Blue Team"
+ "OV_FW_BUILD_PHASE" "BUILD PHASE — %seconds%s | Fortify your base!"
+ "OV_FW_COMBAT_PHASE" "COMBAT PHASE — %seconds%s | Eliminate the enemy!"
+ "OV_FW_WIN_A" "Red Team wins!"
+ "OV_FW_WIN_B" "Blue Team wins!"
+ "OV_FW_DRAW" "Draw!"
+
+ // Traitor Town
+ "OV_TT_TITLE" "Traitor Town"
+ "OV_TT_ROLE_INNOCENT" "You are INNOCENT — find and eliminate the traitors!"
+ "OV_TT_ROLE_TRAITOR" "You are a TRAITOR — eliminate all innocents without being caught!"
+ "OV_TT_ROLE_DETECTIVE" "You are the DETECTIVE — investigate and protect the innocents!"
+ "OV_TT_WIN_INNOCENTS" "Innocents win! All traitors eliminated."
+ "OV_TT_WIN_TRAITORS" "Traitors win! All innocents eliminated."
+ "OV_TT_TRAITOR_REVEAL" "Traitor: %name%"
+
+ // Item types
+ "OV_ITEM_TYPE_PLAYER_MODEL" "Player Model"
+ "OV_ITEM_TYPE_TRAIL" "Trail"
+ "OV_ITEM_TYPE_NAMEPLATE" "Nameplate"
+
+ // XP
+ "OV_XP_LABEL" "XP"
+ "OV_XP_GAINED" "+%xp% XP"
+
+ // Generic
+ "OV_LOADING" "Connecting to server…"
+ "OV_DISCONNECT" "Disconnected from server."
+ "OV_SERVER_FULL" "Server is full. Try again shortly."
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,861 @@
@@ -0,0 +1,861 @@
+// sample animation script
+//
+//
+// commands:
+// Animate <panel name> <variable> <target value> <interpolator> <start time> <duration>
+// variables:
+// FgColor
+// BgColor
+// Position
+// Size
+// Blur (hud panels only)
+// TextColor (hud panels only)
+// Ammo2Color (hud panels only)
+// Alpha (hud weapon selection only)
+// SelectionAlpha (hud weapon selection only)
+// TextScan (hud weapon selection only)
+//
+// interpolator:
+// Linear
+// Accel - starts moving slow, ends fast
+// Deaccel - starts moving fast, ends slow
+//
+// RunEvent <event name> <start time>
+// starts another even running at the specified time
+//
+// StopEvent <event name> <start time>
+// stops another event that is current running at the specified time
+//
+// StopAnimation <panel name> <variable> <start time>
+// stops all animations refering to the specified variable in the specified panel
+//
+// StopPanelAnimations <panel name> <start time>
+// stops all active animations operating on the specified panel
+//
+//
+// Useful game console commands:
+// cl_Animationinfo <hudelement name> or <panelname>
+// displays all the animatable variables for the hud element
+//
+
+
+event LevelInit
+{
+}
+
+event FadeOutTeamLine
+{
+ // make the display visible
+ Animate TeamDisplay Alpha "0" Linear 0.0 0.25
+}
+
+event FadeInTeamLine
+{
+ // make the display visible
+ Animate TeamDisplay Alpha "255" Linear 0.0 0.5
+}
+
+event WeaponHighlight
+{
+ Animate HudWeaponSelection Blur "7" Linear 0.0 0.0
+ Animate HudWeaponSelection Blur "0" Deaccel 0.01 0.75
+
+ Animate HudWeaponSelection SelectedBoxColor "250 220 0 80" Linear 0.0 0.01
+ Animate HudWeaponSelection SelectedBoxColor "BgColor" Deaccel 0.1 1.5
+ Animate HudWeaponSelection SelectedFgColor "BrightFg" Linear 0.0 0.1
+ Animate HudWeaponSelection SelectedFgColor "FgColor" Linear 5.0 1.5
+}
+
+event OpenWeaponSelectionMenu
+{
+ StopEvent CloseWeaponSelectionMenu 0.0
+ StopEvent WeaponPickup 0.0
+ StopEvent FadeOutWeaponSelectionMenu 0.0
+
+ // make the display visible
+ Animate HudWeaponSelection Alpha "128" Linear 0.0 0.1
+ Animate HudWeaponSelection Alpha "192" Linear 0.0 0.1
+ Animate HudWeaponSelection SelectionAlpha "255" Linear 0.0 0.1
+ Animate HudWeaponSelection FgColor "FgColor" Linear 0.0 0.1
+ Animate HudWeaponSelection TextColor "BrightFg" Linear 0.0 0.1
+ Animate HudWeaponSelection TextScan "1" Linear 0.0 0.1
+}
+
+event CloseWeaponSelectionMenu
+{
+ // hide the whole thing near immediately
+ Animate HudWeaponSelection FgColor "0 0 0 0" Linear 0.0 0.1
+ Animate HudWeaponSelection TextColor "0 0 0 0" Linear 0.0 0.1
+ Animate HudWeaponSelection Alpha "0" Linear 0.0 0.1
+ Animate HudWeaponSelection SelectionAlpha "0" Linear 0.0 0.1
+}
+
+event FadeOutWeaponSelectionMenu
+{
+ // slowly hide the whole thing
+ Animate HudWeaponSelection FgColor "0 0 0 0" Linear 0.5 1.0
+ Animate HudWeaponSelection TextColor "0 0 0 0" Linear 0.5 1.0
+ Animate HudWeaponSelection Alpha "0" Linear 0.5 1.0
+ Animate HudWeaponSelection SelectionAlpha "0" Linear 0.5 1.0
+
+ Animate HudWeaponSelection WeaponBoxDist1Alpha "0 0 0 0" Linear 0.0 0.5
+ Animate HudWeaponSelection WeaponBoxDist2Alpha "0 0 0 0" Linear 0.0 0.5
+ Animate HudWeaponSelection WeaponBoxDist3Alpha "0 0 0 0" Linear 0.0 0.5
+}
+
+event SuitAuxPowerMax
+{
+ // hide the suit power
+ Animate HudSuitPower BgColor "0 0 0 0" Linear 0.0 0.4
+ Animate HudSuitPower AuxPowerColor "0 0 0 0" Linear 0.0 0.4
+}
+
+event SuitAuxPowerNotMax
+{
+ // show suit power
+ Animate HudSuitPower BgColor "BgColor" Linear 0.0 0.4
+ Animate HudSuitPower AuxPowerColor "255 220 0 220" Linear 0.0 0.4
+}
+
+event SuitAuxPowerDecreasedBelow25
+{
+ // make color red
+ Animate HudSuitPower AuxPowerColor "255 0 0 220" Linear 0.0 0.4
+}
+
+event SuitAuxPowerIncreasedAbove25
+{
+ // make colr bright
+ Animate HudSuitPower AuxPowerColor "255 220 0 220" Linear 0.0 0.4
+}
+
+event SuitAuxPowerNoItemsActive
+{
+ // resize the aux power to be the smallest size
+ Animate HudSuitPower Size "102 26" Linear 0.0 0.4
+ Animate HudSuitPower Position "16 400" Linear 0.0 0.4
+ Animate HudSuitPower Size "112 30" Linear 0.0 0.4
+ Animate HudSuitPower Position "16 390" Linear 0.0 0.4
+}
+
+event SuitAuxPowerOneItemActive
+{
+ // resize the aux power to fit one item
+ Animate HudSuitPower Size "102 36" Linear 0.0 0.4
+ Animate HudSuitPower Position "16 390" Linear 0.0 0.4
+ Animate HudSuitPower Size "112 44" Linear 0.0 0.4
+ Animate HudSuitPower Position "16 372" Linear 0.0 0.4
+}
+
+event SuitAuxPowerTwoItemsActive
+{
+ // resize the aux power to fit two items
+ Animate HudSuitPower Size "102 46" Linear 0.0 0.4
+ Animate HudSuitPower Position "16 380" Linear 0.0 0.4
+ Animate HudSuitPower Size "112 56" Linear 0.0 0.4
+ Animate HudSuitPower Position "16 364" Linear 0.0 0.4
+}
+
+event SuitAuxPowerThreeItemsActive
+{
+ // resize the aux power to fit three items
+ Animate HudSuitPower Size "102 56" Linear 0.0 0.4
+ Animate HudSuitPower Position "16 370" Linear 0.0 0.4
+ Animate HudSuitPower Size "112 70" Linear 0.0 0.4
+ Animate HudSuitPower Position "16 350" Linear 0.0 0.4
+}
+
+event SuitFlashlightOn
+{
+ Animate HudFlashlight TextColor "255 220 0 255" Linear 0.0 0.1
+ Animate HudFlashlight TextColor "255 220 0 160" Deaccel 0.1 0.75
+ Animate HudFlashlight BgColor "BgColor" Linear 0.0 0.75
+}
+
+event SuitFlashlightOff
+{
+ StopEvent SuitFlashlightOn 0.0
+ Animate HudFlashlight TextColor "0 0 0 0" Linear 0.0 0.4
+ Animate HudFlashlight BgColor "0 0 0 0" Linear 0.0 0.4
+}
+
+event HudTakeDamageFront
+{
+}
+
+event HudTakeDamageLeft
+{
+ Animate HudDamageIndicator DmgColorLeft "255 88 0 200" Linear 0.0 0.0
+ Animate HudDamageIndicator DmgColorLeft "255 0 0 200" Linear 0.0 0.3
+ Animate HudDamageIndicator DmgColorLeft "255 0 0 0" Deaccel 0.3 0.5
+}
+
+event HudTakeDamageRight
+{
+ Animate HudDamageIndicator DmgColorRight "255 88 0 200" Linear 0.0 0.0
+ Animate HudDamageIndicator DmgColorRight "255 0 0 200" Linear 0.0 0.3
+ Animate HudDamageIndicator DmgColorRight "255 0 0 0" Deaccel 0.3 0.5
+}
+
+event HudTakeDamageBehind
+{
+ RunEvent HudTakeDamageLeft 0.0
+ RunEvent HudTakeDamageRight 0.0
+}
+
+event HudTakeDamageHighLeft
+{
+ Animate HudDamageIndicator DmgHighColorLeft "255 88 0 255" Linear 0.0 0.0
+ Animate HudDamageIndicator DmgHighColorLeft "255 0 0 200" Linear 0.0 0.4
+ Animate HudDamageIndicator DmgHighColorLeft "255 0 0 0" Deaccel 0.4 2.4
+}
+
+event HudTakeDamageHighRight
+{
+ Animate HudDamageIndicator DmgHighColorRight "255 88 0 255" Linear 0.0 0.0
+ Animate HudDamageIndicator DmgHighColorRight "255 0 0 200" Linear 0.0 0.4
+ Animate HudDamageIndicator DmgHighColorRight "255 0 0 0" Deaccel 0.4 2.4
+}
+
+event HudTakeDamageHigh
+{
+ Animate HudDamageIndicator DmgFullscreenColor "255 88 0 200" Linear 0.0 0.0
+ Animate HudDamageIndicator DmgFullscreenColor "255 0 0 200" Linear 0.0 0.4
+ Animate HudDamageIndicator DmgFullscreenColor "255 0 0 0" Deaccel 0.4 2.4
+}
+
+event HudTakeDamageDrown
+{
+ RunEvent HudTakeDamageBehind 0.0
+}
+
+event HudTakeDamagePoison
+{
+ Animate HudDamageIndicator DmgFullscreenColor "255 236 128 240" Linear 0.0 0.0
+ Animate HudDamageIndicator DmgFullscreenColor "255 236 128 0" Deaccel 0.4 0.8
+}
+
+event HudTakeDamageBurn
+{
+ Animate HudDamageIndicator DmgFullscreenColor "255 0 0 200" Linear 0.0 0.0
+ Animate HudDamageIndicator DmgFullscreenColor "255 0 0 0" Deaccel 0.2 0.4
+}
+
+event HudTakeDamageRadiation
+{
+ Animate HudDamageIndicator DmgFullscreenColor "255 255 255 128" Deaccel 0.0 0.1
+ Animate HudDamageIndicator DmgFullscreenColor "255 255 255 0" Deaccel 0.1 0.4
+ RunEvent HudTakeDamageBehind 0.0
+}
+
+event HudPlayerDeath
+{
+ StopEvent HealthLoop 0.0
+ StopEvent HealthPulse 0.0
+
+ Animate HudDamageIndicator DmgFullscreenColor "255 0 0 96" Deaccel 0.1 0.2
+ Animate HudDamageIndicator DmgFullscreenColor "255 0 0 255" Deaccel 0.3 4.0
+}
+
+event HealthIncreasedAbove20
+{
+ StopEvent HealthLoop 0.0
+ StopEvent HealthPulse 0.0
+ StopEvent HealthLow 0.0
+
+ Animate HudHealth BgColor "BgColor" Linear 0.0 0.0
+
+ Animate HudHealth TextColor "FgColor" Linear 0.0 0.04
+ Animate HudHealth FgColor "FgColor" Linear 0.0 0.03
+
+ Animate HudHealth Blur "3" Linear 0.0 0.1
+ Animate HudHealth Blur "0" Deaccel 0.1 2.0
+}
+
+event HealthIncreasedBelow20
+{
+ Animate HudHealth FgColor "BrightFg" Linear 0.0 0.25
+ Animate HudHealth FgColor "FgColor" Linear 0.3 0.75
+
+ Animate HudHealth Blur "3" Linear 0.0 0.1
+ Animate HudHealth Blur "0" Deaccel 0.1 2.0
+}
+
+event SuitPowerIncreasedAbove20
+{
+ StopEvent SuitLoop 0.0
+ StopEvent SuitPulse 0.0
+ StopEvent SuitPowerZero 0.0
+
+ Animate HudSuit Alpha "255" Linear 0.0 0.0
+
+ Animate HudSuit BgColor "BgColor" Linear 0.0 0.0
+
+ Animate HudSuit TextColor "FgColor" Linear 0.0 0.05
+ Animate HudSuit FgColor "FgColor" Linear 0.0 0.05
+
+ Animate HudSuit Blur "3" Linear 0.0 0.1
+ Animate HudSuit Blur "0" Deaccel 0.1 2.0
+}
+
+
+event SuitPowerIncreasedBelow20
+{
+ StopEvent SuitPowerZero 0.0
+ Animate HudSuit Alpha "255" Linear 0.0 0.0
+
+ Animate HudSuit FgColor "BrightFg" Linear 0.0 0.25
+ Animate HudSuit FgColor "FgColor" Linear 0.3 0.75
+
+ Animate HudSuit Blur "3" Linear 0.0 0.1
+ Animate HudSuit Blur "0" Deaccel 0.1 2.0
+}
+
+event SuitPowerZero
+{
+ StopEvent SuitLoop 0.0
+ StopEvent SuitPulse 0.0
+ StopEvent SuitArmorLow 0.0
+ StopEvent SuitDamageTaken 0.0
+
+ Animate HudSuit Alpha "0" Linear 0.0 0.4
+}
+
+event TestMovement
+{
+ Animate HudHealth Position "256 120" Linear 0.0 2.0
+ Animate HudHealth Size "128 24" Linear 0.0 1.5
+}
+
+event HealthDamageTaken
+{
+ Animate HudHealth FgColor "BrightFg" Linear 0.0 0.25
+ Animate HudHealth FgColor "FgColor" Linear 0.3 0.75
+
+ Animate HudHealth Blur "3" Linear 0.0 0.1
+ Animate HudHealth Blur "0" Deaccel 0.1 2.0
+
+ Animate HudHealth TextColor "BrightFg" Linear 0.0 0.1
+ Animate HudHealth TextColor "FgColor" Deaccel 0.1 1.2
+}
+
+event SuitDamageTaken
+{
+ Animate HudSuit FgColor "BrightFg" Linear 0.0 0.25
+ Animate HudSuit FgColor "FgColor" Linear 0.3 0.75
+
+ Animate HudSuit Blur "3" Linear 0.0 0.1
+ Animate HudSuit Blur "0" Deaccel 0.1 2.0
+
+ Animate HudSuit TextColor "BrightFg" Linear 0.0 0.1
+ Animate HudSuit TextColor "FgColor" Deaccel 0.1 1.2
+}
+
+// health has been damaged to below 20%
+event HealthLow
+{
+ StopEvent HealthDamageTaken 0.0
+ StopEvent HealthPulse 0.0
+ StopEvent HealthLoop 0.0
+
+ Animate HudHealth BgColor "DamagedBg" Linear 0.0 0.1
+ Animate HudHealth BgColor "BgColor" Deaccel 0.1 1.75
+
+ Animate HudHealth FgColor "BrightFg" Linear 0.0 0.2
+ Animate HudHealth FgColor "DamagedFg" Linear 0.2 1.2
+
+ Animate HudHealth TextColor "BrightFg" Linear 0.0 0.1
+ Animate HudHealth TextColor "DamagedFg" Linear 0.1 1.2
+
+ Animate HudHealth Blur "5" Linear 0.0 0.1
+ Animate HudHealth Blur "3" Deaccel 0.1 0.9
+
+ RunEvent HealthPulse 1.0
+}
+
+event HealthPulse
+{
+ Animate HudHealth Blur "5" Linear 0.0 0.1
+ Animate HudHealth Blur "2" Deaccel 0.1 0.8
+ Animate HudHealth TextColor "BrightDamagedFg" Linear 0.0 0.1
+ Animate HudHealth TextColor "DamagedFg" Deaccel 0.1 0.8
+ Animate HudHealth BgColor "100 0 0 80" Linear 0.0 0.1
+ Animate HudHealth BgColor "BgColor" Deaccel 0.1 0.8
+
+ RunEvent HealthLoop 0.8
+}
+
+// call to loop HealthPulse
+event HealthLoop
+{
+ RunEvent HealthPulse 0.0
+}
+
+
+// suit armor has been damaged to below 20%
+event SuitArmorLow
+{
+ StopEvent SuitDamageTaken 0.0
+ StopEvent SuitPulse 0.0
+ StopEvent SuitLoop 0.0
+
+// removing this effect -- matching it to the event for normal suit damage
+// so, there will be no special indication (redness, flashing)
+// in the hud that the suit armor is low
+
+// Animate HudSuit BgColor "DamagedBg" Linear 0.0 0.1
+// Animate HudSuit BgColor "BgColor" Deaccel 0.1 1.75
+
+ Animate HudSuit FgColor "BrightFg" Linear 0.0 0.25
+ Animate HudSuit FgColor "FgColor" Linear 0.3 0.75
+
+ Animate HudSuit Blur "3" Linear 0.0 0.1
+ Animate HudSuit Blur "0" Deaccel 0.1 2.0
+
+ Animate HudSuit TextColor "BrightFg" Linear 0.0 0.1
+ Animate HudSuit TextColor "FgColor" Deaccel 0.1 1.2
+
+// RunEvent SuitPulse 1.0
+}
+
+event SuitPulse
+// this even no longer gets called
+{
+ Animate HudSuit Blur "5" Linear 0.0 0.1
+ Animate HudSuit Blur "2" Deaccel 0.1 0.8
+ Animate HudSuit TextColor "BrightDamagedFg" Linear 0.0 0.1
+ Animate HudSuit TextColor "DamagedFg" Deaccel 0.1 0.8
+ Animate HudSuit BgColor "100 0 0 80" Linear 0.0 0.1
+ Animate HudSuit BgColor "BgColor" Deaccel 0.1 0.8
+ RunEvent SuitLoop 0.8
+}
+
+event SuitLoop
+{
+// this event no longer gets called
+ RunEvent SuitPulse 0.0
+}
+
+// ammo has been picked up
+event AmmoIncreased
+{
+ Animate HudAmmo FgColor "BrightFg" Linear 0.0 0.15
+ Animate HudAmmo FgColor "FgColor" Deaccel 0.15 1.5
+ Animate HudAmmo Blur "5" Linear 0.0 0.0
+ Animate HudAmmo Blur "0" Accel 0.01 1.5
+}
+
+// ammo has been decreased, but there is still some remaining
+event AmmoDecreased
+{
+ StopEvent AmmoIncreased 0.0
+
+ Animate HudAmmo Blur "7" Linear 0.0 0.0
+ Animate HudAmmo Blur "0" Deaccel 0.1 1.5
+
+ Animate HudAmmo TextColor "BrightFg" Linear 0.0 0.1
+ Animate HudAmmo TextColor "FgColor" Deaccel 0.1 0.75
+}
+
+// primary ammo is zero
+event AmmoEmpty
+{
+ Animate Hudammo FgColor "BrightDamagedFg" Linear 0.0 0.2
+ Animate Hudammo FgColor "DamagedFg" Accel 0.2 1.2
+}
+
+// ammo2 is the total ammo for a weapon that uses clip ammo
+event Ammo2Increased
+{
+ Animate Hudammo ammo2color "BrightFg" Linear 0.0 0.2
+ Animate Hudammo ammo2color "FgColor" Accel 0.2 1.2
+}
+
+// total ammo has been decreased, but there is still some remaining
+event Ammo2Decreased
+{
+ Animate Hudammo ammo2color "BrightFg" Linear 0.0 0.2
+ Animate Hudammo ammo2color "FgColor" Accel 0.2 1.2
+}
+
+// total ammo is zero
+event Ammo2Empty
+{
+ Animate Hudammo ammo2color "BrightDamagedFg" Linear 0.0 0.2
+ Animate Hudammo ammo2color "DamagedFg" Accel 0.2 1.2
+}
+
+event AmmoSecondaryIncreased
+{
+ Animate HudAmmoSecondary FgColor "BrightFg" Linear 0.0 0.15
+ Animate HudAmmoSecondary FgColor "FgColor" Deaccel 0.15 1.5
+ Animate HudAmmoSecondary Blur "5" Linear 0.0 0.0
+ Animate HudAmmoSecondary Blur "0" Accel 0.01 1.5
+}
+
+event AmmoSecondaryDecreased
+{
+ StopEvent AmmoSecondaryIncreased 0.0
+
+ Animate HudAmmoSecondary Blur "7" Linear 0.0 0.0
+ Animate HudAmmoSecondary Blur "0" Deaccel 0.1 1.5
+
+ Animate HudAmmoSecondary TextColor "BrightFg" Linear 0.0 0.1
+ Animate HudAmmoSecondary TextColor "FgColor" Deaccel 0.1 0.75
+}
+
+event AmmoSecondaryEmpty
+{
+ Animate HudAmmoSecondary FgColor "BrightDamagedFg" Linear 0.0 0.2
+ Animate HudAmmoSecondary FgColor "DamagedFg" Accel 0.2 1.2
+ Animate HudAmmoSecondary Blur "7" Linear 0.0 0.0
+ Animate HudAmmoSecondary Blur "0" Deaccel 0.1 1.5
+
+}
+
+// current weapon has been changed
+event WeaponChanged
+{
+ Animate HudAmmo BgColor "250 220 0 80" Linear 0.0 0.1
+ Animate HudAmmo BgColor "BgColor" Deaccel 0.1 1.0
+ Animate HudAmmo FgColor "BrightFg" Linear 0.0 0.1
+ Animate HudAmmo FgColor "FgColor" Linear 0.2 1.5
+}
+
+// ran if we just changed to a weapon that needs clip ammo
+event WeaponUsesClips
+{
+ Animate HudAmmo Position "r150 432" Deaccel 0.0 0.4
+ Animate HudAmmo Size "132 36" Deaccel 0.0 0.4
+}
+event WeaponUsesClips
+{
+ Animate HudAmmo Position "r162 426" Deaccel 0.0 0.4
+ Animate HudAmmo Size "144 42" Deaccel 0.0 0.4
+}
+
+// ran if we just changed to a weapon that does not use clip ammo
+event WeaponDoesNotUseClips
+{
+ Animate HudAmmo Position "r118 432" Deaccel 0.0 0.4
+ Animate HudAmmo Size "100 36" Deaccel 0.0 0.4
+}
+event WeaponDoesNotUseClips
+{
+ Animate HudAmmo Position "r130 426" Deaccel 0.0 0.4
+ Animate HudAmmo Size "120 42" Deaccel 0.0 0.4
+}
+
+event WeaponUsesSecondaryAmmo
+{
+ StopAnimation HudAmmo Position 0.0
+ StopAnimation HudAmmo Size 0.0
+ StopPanelAnimations HudAmmoSecondary 0.0
+ //StopPanelAnimations HudAmmo 0.0
+
+ Animate HudAmmoSecondary BgColor "250 220 0 60" Linear 0.0 0.1
+ Animate HudAmmoSecondary BgColor "BgColor" Deaccel 0.1 1.0
+ Animate HudAmmoSecondary FgColor "BrightFg" Linear 0.0 0.1
+ Animate HudAmmoSecondary FgColor "FgColor" Linear 0.2 1.5
+ Animate HudAmmoSecondary Alpha 255 Linear 0.0 0.1
+
+ Animate HudAmmo Position "r222 432" Deaccel 0.0 0.5
+ Animate HudAmmo Size "132 36" Deaccel 0.0 0.4
+ Animate HudAmmo Position "r234 426" Deaccel 0.0 0.5
+ Animate HudAmmo Size "144 42" Deaccel 0.0 0.4
+}
+
+event WeaponDoesNotUseSecondaryAmmo
+{
+ StopPanelAnimations HudAmmoSecondary 0.0
+ Animate HudAmmoSecondary FgColor "0 0 0 0" Linear 0.0 0.4
+ Animate HudAmmoSecondary BgColor "0 0 0 0" Linear 0.0 0.4
+ Animate HudAmmoSecondary Alpha 0 Linear 0.0 0.1
+}
+
+event CraneMagnetFlash
+{
+ Animate HudCraneMagnet TextColor "255 220 0 255" Linear 0.0 0.1
+ Animate HudCraneMagnet TextColor "255 220 0 160" Deaccel 0.1 0.3
+ Animate HudCraneMagnet TextColor "255 170 0 220" Deaccel 0.4 0.3
+ Animate HudCraneMagnet TextColor "255 220 0 255" Linear 0.8 0.2
+}
+
+event HintMessageShow
+{
+ StopPanelAnimations HudHintDisplay 0.0
+
+ Animate HudHintDisplay HintSize "1" Deaccel 0.0 0.3
+ Animate HudHintDisplay FgColor "FgColor" Linear 0.4 0.4
+
+ // flash text
+ Animate HudHintDisplay FgColor "FgColor" Linear 1.5 0.01
+ Animate HudHintDisplay FgColor "255 220 0 255" Linear 2.0 0.2
+ Animate HudHintDisplay FgColor "FgColor" Linear 2.2 0.2
+ Animate HudHintDisplay FgColor "255 220 0 255" Linear 3.0 0.2
+ Animate HudHintDisplay FgColor "FgColor" Linear 3.2 0.2
+
+ // hide the panel after a while
+ Animate HudHintDisplay FgColor "255 220 0 0" Linear 10.0 0.2
+ Animate HudHintDisplay HintSize "0" Deaccel 10.2 0.3
+}
+
+
+event HintMessageHide
+{
+ Animate HudHintDisplay FgColor "255 220 0 0" Linear 0.0 0.2
+ Animate HudHintDisplay HintSize "0" Deaccel 0.2 0.3
+}
+
+event KeyHintMessageShow
+{
+ StopPanelAnimations HudHintKeyDisplay 0.0
+
+ // show the hints
+ Animate HudHintKeyDisplay Alpha 255 Linear 0.0 0.5
+
+ // flash text
+ Animate HudHintKeyDisplay FgColor "FgColor" Linear 0.0 0.01
+ Animate HudHintKeyDisplay FgColor "255 220 0 255" Linear 0.5 0.2
+ Animate HudHintKeyDisplay FgColor "FgColor" Linear 0.7 0.2
+ Animate HudHintKeyDisplay FgColor "255 220 0 255" Linear 1.5 0.2
+ Animate HudHintKeyDisplay FgColor "FgColor" Linear 1.7 0.2
+
+ // hide the panel after a while
+ Animate HudHintKeyDisplay Alpha 0 Linear 12.0 1.0
+}
+
+event KeyHintMessageHide
+{
+ Animate HudHintKeyDisplay Alpha 0 Linear 0.0 0.5
+}
+
+
+event SquadMemberAdded
+{
+ StopEvent SquadMemberDied 0.0
+ StopEvent SquadMemberLeft 0.0
+
+ // add in the squad member, brighter then normal color
+ Animate HudSquadStatus LastMemberColor "255 220 0 255" Linear 0.0 0.3
+ Animate HudSquadStatus LastMemberColor "255 220 0 160" Linear 0.3 0.3
+}
+
+event SquadMemberLeft
+{
+ StopEvent SquadMemberDied 0.0
+ StopEvent SquadMemberAdded 0.0
+
+ // fade out the icon
+ Animate HudSquadStatus LastMemberColor "255 220 0 0" Linear 0.0 0.5
+}
+
+event SquadMemberDied
+{
+ StopEvent SquadMemberAdded 0.0
+ StopEvent SquadMemberLeft 0.0
+
+ // flash red, hold, then disappear
+ Animate HudSquadStatus LastMemberColor "255 0 0 255" Linear 0.0 0.5
+ Animate HudSquadStatus LastMemberColor "255 0 0 0" Linear 2.0 2.0
+}
+
+event SquadMembersFollowing
+{
+ StopEvent SquadMembersStationed 0.0
+ Animate HudSquadStatus SquadTextColor "255 220 0 255" Linear 0.0 0.2
+ Animate HudSquadStatus SquadTextColor "255 220 0 160" Linear 0.2 0.4
+}
+
+event SquadMembersStationed
+{
+ StopEvent SquadMembersFollowing 0.0
+ Animate HudSquadStatus SquadTextColor "255 220 0 160" Linear 0.5 0.5
+}
+
+event PoisonDamageTaken
+{
+ Animate HudPoisonDamageIndicator Alpha 255 Linear 0.0 1.0
+ RunEvent PoisonLoop 0.0
+}
+
+event PoisonDamageCured
+{
+ StopEvent PoisonDamageTaken 0.0
+ StopEvent PoisonLoop 0.0
+ StopEvent PoisonPulse 0.0
+ Animate HudPoisonDamageIndicator Alpha 0 Linear 0.0 1.0
+}
+
+event PoisonPulse
+{
+ Animate HudPoisonDamageIndicator TextColor "BrightFg" Linear 0.0 0.1
+ Animate HudPoisonDamageIndicator TextColor "FgColor" Deaccel 0.1 0.8
+ Animate HudPoisonDamageIndicator BgColor "100 0 0 80" Linear 0.0 0.1
+ Animate HudPoisonDamageIndicator BgColor "BgColor" Deaccel 0.1 0.8
+
+ RunEvent PoisonLoop 0.8
+}
+
+// call to loop PoisonLoop
+event PoisonLoop
+{
+ RunEvent PoisonPulse 0.0
+}
+
+// Commentary hud element
+event ShowCommentary
+{
+ StopEvent HideCommentary 0.0
+ Animate HudCommentary Alpha 255 Linear 0.0 0.5
+}
+
+event HideCommentary
+{
+ StopEvent ShowCommentary 0.0
+ Animate HudCommentary Alpha 0 Linear 0.0 0.5
+}
+
+// Voting
+event PulseOption1
+{
+ Animate Option2Background_Selected Alpha 0 Linear 0.0 0.001
+ Animate Option3Background_Selected Alpha 0 Linear 0.0 0.001
+ Animate Option4Background_Selected Alpha 0 Linear 0.0 0.001
+ Animate Option5Background_Selected Alpha 0 Linear 0.0 0.001
+
+ Animate Option1Background_Selected Alpha 255 Linear 0.0 0.001
+ Animate Option1Background_Selected Alpha 128 Linear 0.5 1.5
+
+ Animate YesLeftLabel Alpha 0 Linear 1.25 1.7
+ Animate YesIcon Alpha 0 Linear 1.25 1.7
+ Animate YesRightLabel Alpha 0 Linear 1.25 1.7
+
+ Animate NoLeftLabel Alpha 0 Linear 1.25 1.7
+ Animate NoIcon Alpha 0 Linear 1.25 1.7
+ Animate NoRightLabel Alpha 0 Linear 1.25 1.7
+
+ Animate LabelOption2 Alpha 60 Linear 1.25 1.7
+ Animate LabelOption3 Alpha 60 Linear 1.25 1.7
+ Animate LabelOption4 Alpha 60 Linear 1.25 1.7
+ Animate LabelOption5 Alpha 60 Linear 1.25 1.7
+}
+
+event PulseOption2
+{
+ Animate Option1Background_Selected Alpha 0 Linear 0.0 0.001
+ Animate Option3Background_Selected Alpha 0 Linear 0.0 0.001
+ Animate Option4Background_Selected Alpha 0 Linear 0.0 0.001
+ Animate Option5Background_Selected Alpha 0 Linear 0.0 0.001
+
+ Animate Option2Background_Selected Alpha 255 Linear 0.0 0.001
+ Animate Option2Background_Selected Alpha 128 Linear 0.5 1.5
+
+ Animate YesLeftLabel Alpha 0 Linear 1.25 1.7
+ Animate YesIcon Alpha 0 Linear 1.25 1.7
+ Animate YesRightLabel Alpha 0 Linear 1.25 1.7
+
+ Animate NoLeftLabel Alpha 0 Linear 1.25 1.7
+ Animate NoIcon Alpha 0 Linear 1.25 1.7
+ Animate NoRightLabel Alpha 0 Linear 1.25 1.7
+
+ Animate LabelOption1 Alpha 60 Linear 1.25 1.7
+ Animate LabelOption3 Alpha 60 Linear 1.25 1.7
+ Animate LabelOption4 Alpha 60 Linear 1.25 1.7
+ Animate LabelOption5 Alpha 60 Linear 1.25 1.7
+}
+
+event PulseOption3
+{
+ Animate Option1Background_Selected Alpha 0 Linear 0.0 0.001
+ Animate Option2Background_Selected Alpha 0 Linear 0.0 0.001
+ Animate Option4Background_Selected Alpha 0 Linear 0.0 0.001
+ Animate Option5Background_Selected Alpha 0 Linear 0.0 0.001
+
+ Animate Option3Background_Selected Alpha 255 Linear 0.0 0.001
+ Animate Option3Background_Selected Alpha 128 Linear 0.5 1.5
+
+ Animate YesLeftLabel Alpha 0 Linear 1.25 1.7
+ Animate YesIcon Alpha 0 Linear 1.25 1.7
+ Animate YesRightLabel Alpha 0 Linear 1.25 1.7
+
+ Animate NoLeftLabel Alpha 0 Linear 1.25 1.7
+ Animate NoIcon Alpha 0 Linear 1.25 1.7
+ Animate NoRightLabel Alpha 0 Linear 1.25 1.7
+
+ Animate LabelOption1 Alpha 60 Linear 1.25 1.7
+ Animate LabelOption2 Alpha 60 Linear 1.25 1.7
+ Animate LabelOption4 Alpha 60 Linear 1.25 1.7
+ Animate LabelOption5 Alpha 60 Linear 1.25 1.7
+}
+
+event PulseOption4
+{
+ Animate Option1Background_Selected Alpha 0 Linear 0.0 0.001
+ Animate Option2Background_Selected Alpha 0 Linear 0.0 0.001
+ Animate Option3Background_Selected Alpha 0 Linear 0.0 0.001
+ Animate Option5Background_Selected Alpha 0 Linear 0.0 0.001
+
+ Animate Option4Background_Selected Alpha 255 Linear 0.0 0.001
+ Animate Option4Background_Selected Alpha 128 Linear 0.5 1.5
+
+ Animate YesLeftLabel Alpha 0 Linear 1.25 1.7
+ Animate YesIcon Alpha 0 Linear 1.25 1.7
+ Animate YesRightLabel Alpha 0 Linear 1.25 1.7
+
+ Animate NoLeftLabel Alpha 0 Linear 1.25 1.7
+ Animate NoIcon Alpha 0 Linear 1.25 1.7
+ Animate NoRightLabel Alpha 0 Linear 1.25 1.7
+
+ Animate LabelOption1 Alpha 60 Linear 1.25 1.7
+ Animate LabelOption2 Alpha 60 Linear 1.25 1.7
+ Animate LabelOption3 Alpha 60 Linear 1.25 1.7
+ Animate LabelOption5 Alpha 60 Linear 1.25 1.7
+}
+
+event PulseOption5
+{
+ Animate Option1Background_Selected Alpha 0 Linear 0.0 0.001
+ Animate Option2Background_Selected Alpha 0 Linear 0.0 0.001
+ Animate Option3Background_Selected Alpha 0 Linear 0.0 0.001
+ Animate Option4Background_Selected Alpha 0 Linear 0.0 0.001
+
+ Animate Option5Background_Selected Alpha 255 Linear 0.0 0.001
+ Animate Option5Background_Selected Alpha 128 Linear 0.5 1.5
+
+ Animate YesLeftLabel Alpha 0 Linear 1.25 1.7
+ Animate YesIcon Alpha 0 Linear 1.25 1.7
+ Animate YesRightLabel Alpha 0 Linear 1.25 1.7
+
+ Animate NoLeftLabel Alpha 0 Linear 1.25 1.7
+ Animate NoIcon Alpha 0 Linear 1.25 1.7
+ Animate NoRightLabel Alpha 0 Linear 1.25 1.7
+
+ Animate LabelOption1 Alpha 60 Linear 1.25 1.7
+ Animate LabelOption2 Alpha 60 Linear 1.25 1.7
+ Animate LabelOption3 Alpha 60 Linear 1.25 1.7
+ Animate LabelOption4 Alpha 60 Linear 1.25 1.7
+}
+
+event HideVoteBackgrounds
+{
+ Animate Option1Background_Selected Alpha 0 Linear 0.0 0.001
+ Animate Option2Background_Selected Alpha 0 Linear 0.0 0.001
+ Animate Option3Background_Selected Alpha 0 Linear 0.0 0.001
+ Animate Option4Background_Selected Alpha 0 Linear 0.0 0.001
+ Animate Option5Background_Selected Alpha 0 Linear 0.0 0.001
+
+ Animate YesLeftLabel Alpha 255 Linear 0.0 0.001
+ Animate YesIcon Alpha 255 Linear 0.0 0.001
+ Animate YesRightLabel Alpha 255 Linear 0.0 0.001
+
+ Animate NoLeftLabel Alpha 255 Linear 0.0 0.001
+ Animate NoIcon Alpha 255 Linear 0.0 0.001
+ Animate NoRightLabel Alpha 255 Linear 0.0 0.001
+
+ Animate LabelOption1 Alpha 255 Linear 0.0 0.001
+ Animate LabelOption2 Alpha 255 Linear 0.0 0.001
+ Animate LabelOption3 Alpha 255 Linear 0.0 0.001
+ Animate LabelOption4 Alpha 255 Linear 0.0 0.001
+ Animate LabelOption5 Alpha 255 Linear 0.0 0.001
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,758 @@
@@ -0,0 +1,758 @@
+"Resource/HudLayout.res"
+{
+ HudHealth
+ {
+ "fieldName" "HudHealth"
+ "xpos" "16"
+ "ypos" "432"
+ "wide" "102"
+ "tall" "36"
+ "visible" "1"
+ "enabled" "1"
+
+ "PaintBackgroundType" "2"
+
+ "text_xpos" "8"
+ "text_ypos" "20"
+ "digit_xpos" "50"
+ "digit_ypos" "2"
+ }
+ HudHealth
+ {
+ "fieldName" "HudHealth"
+ "xpos" "16"
+ "ypos" "426"
+ "wide" "130"
+ "tall" "42"
+ "visible" "1"
+ "enabled" "1"
+
+ "PaintBackgroundType" "2"
+
+ "text_xpos" "8"
+ "text_ypos" "23"
+ "digit_xpos" "66"
+ "digit_ypos" "0"
+ }
+
+ TargetID
+ {
+ "fieldName" "TargetID"
+ "visible" "1"
+ "enabled" "1"
+ "wide" "640"
+ "tall" "480"
+ }
+
+ TeamDisplay
+ {
+ "fieldName" "TeamDisplay"
+ "visible" "0"
+ "enabled" "1"
+ "xpos" "16"
+ "ypos" "410"
+ "ypos" "400"
+ "wide" "200"
+ "tall" "60"
+ "text_xpos" "8"
+ "text_ypos" "4"
+ }
+
+ HudVoiceSelfStatus
+ {
+ "fieldName" "HudVoiceSelfStatus"
+ "visible" "1"
+ "enabled" "1"
+ "xpos" "r43"
+ "ypos" "355"
+ "wide" "24"
+ "tall" "24"
+ }
+
+ HudVoiceStatus
+ {
+ "fieldName" "HudVoiceStatus"
+ "visible" "1"
+ "enabled" "1"
+ "xpos" "r145"
+ "ypos" "0"
+ "wide" "145"
+ "tall" "400"
+
+ "item_wide" "135"
+
+ "show_avatar" "0"
+
+ "show_dead_icon" "1"
+ "dead_xpos" "1"
+ "dead_ypos" "0"
+ "dead_wide" "16"
+ "dead_tall" "16"
+
+ "show_voice_icon" "1"
+ "icon_ypos" "0"
+ "icon_xpos" "15"
+ "icon_tall" "16"
+ "icon_wide" "16"
+
+ "text_xpos" "33"
+ }
+
+ HudSuit
+ {
+ "fieldName" "HudSuit"
+ "xpos" "140"
+ "ypos" "432"
+ "wide" "108"
+ "tall" "36"
+ "visible" "1"
+ "enabled" "1"
+
+ "PaintBackgroundType" "2"
+
+ "text_xpos" "8"
+ "text_ypos" "20"
+ "digit_xpos" "50"
+ "digit_ypos" "2"
+ }
+ HudSuit
+ {
+ "fieldName" "HudSuit"
+ "xpos" "150"
+ "ypos" "426"
+ "wide" "120"
+ "tall" "42"
+ "visible" "1"
+ "enabled" "1"
+
+ "PaintBackgroundType" "2"
+
+ "text_xpos" "8"
+ "text_ypos" "23"
+ "digit_xpos" "56"
+ "digit_ypos" "0"
+ }
+
+
+ HudAmmo
+ {
+ "fieldName" "HudAmmo"
+ "xpos" "r150"
+ "ypos" "432"
+ "wide" "136"
+ "tall" "36"
+ "visible" "1"
+ "enabled" "1"
+
+ "PaintBackgroundType" "2"
+
+ "text_xpos" "8"
+ "text_ypos" "20"
+ "digit_xpos" "44"
+ "digit_ypos" "2"
+ "digit2_xpos" "98"
+ "digit2_ypos" "16"
+ }
+ HudAmmo
+ {
+ "fieldName" "HudAmmo"
+ "xpos" "r150"
+ "ypos" "426"
+ "wide" "152"
+ "tall" "42"
+ "visible" "1"
+ "enabled" "1"
+
+ "PaintBackgroundType" "2"
+
+ "text_xpos" "8"
+ "text_ypos" "24"
+ "digit_xpos" "55"
+ "digit_ypos" "0"
+ "digit2_xpos" "100"
+ "digit2_ypos" "12"
+ }
+
+ HudAmmoSecondary
+ {
+ "fieldName" "HudAmmoSecondary"
+ "xpos" "r76"
+ "ypos" "432"
+ "wide" "60"
+ "tall" "36"
+ "visible" "1"
+ "enabled" "1"
+
+ "PaintBackgroundType" "2"
+
+ "text_xpos" "8"
+ "text_ypos" "22"
+ "digit_xpos" "36"
+ "digit_ypos" "2"
+ }
+ HudAmmoSecondary
+ {
+ "fieldName" "HudAmmoSecondary"
+ "xpos" "r82"
+ "ypos" "426"
+ "wide" "70"
+ "tall" "42"
+ "visible" "1"
+ "enabled" "1"
+
+ "PaintBackgroundType" "2"
+
+ "text_xpos" "8"
+ "text_ypos" "24"
+ "digit_xpos" "42"
+ "digit_ypos" "0"
+ }
+
+ HudSuitPower
+ {
+ "fieldName" "HudSuitPower"
+ "visible" "1"
+ "enabled" "1"
+ "xpos" "16"
+ "ypos" "396"
+ "wide" "102"
+ "tall" "26"
+
+ "AuxPowerLowColor" "255 0 0 220"
+ "AuxPowerHighColor" "255 220 0 220"
+ "AuxPowerDisabledAlpha" "70"
+
+ "BarInsetX" "8"
+ "BarInsetY" "15"
+ "BarWidth" "92"
+ "BarHeight" "4"
+ "BarChunkWidth" "6"
+ "BarChunkGap" "3"
+
+ "text_xpos" "8"
+ "text_ypos" "4"
+ "text2_xpos" "8"
+ "text2_ypos" "22"
+ "text2_gap" "10"
+
+ "PaintBackgroundType" "2"
+ }
+
+ HudSuitPower
+ {
+ "fieldName" "HudSuitPower"
+ "visible" "1"
+ "enabled" "1"
+ "xpos" "16"
+ "ypos" "386"
+ "wide" "112"
+ "tall" "54"
+
+ "AuxPowerLowColor" "255 0 0 220"
+ "AuxPowerHighColor" "255 220 0 220"
+ "AuxPowerDisabledAlpha" "70"
+
+ "BarInsetX" "8"
+ "BarInsetY" "18"
+ "BarWidth" "102"
+ "BarHeight" "5"
+ "BarChunkWidth" "6"
+ "BarChunkGap" "3"
+
+ "text_xpos" "8"
+ "text_ypos" "4"
+ "text2_xpos" "8"
+ "text2_ypos" "26"
+ "text2_gap" "14"
+
+ "PaintBackgroundType" "2"
+ }
+
+ HudPosture
+ {
+ "fieldName" "HudPosture"
+ "visible" "1"
+ "PaintBackgroundType" "2"
+ "xpos" "16"
+ "ypos" "316"
+ "tall" "35"
+ "wide" "36"
+ "font" "WeaponIconsSmall"
+ "icon_xpos" "8"
+ "icon_ypos" "-2"
+ }
+ HudPosture
+ {
+ "fieldName" "HudPosture"
+ "visible" "1"
+ "PaintBackgroundType" "2"
+ "xpos" "48"
+ "ypos" "316"
+ "tall" "36"
+ "wide" "36"
+ "font" "WeaponIconsSmall"
+ "icon_xpos" "10"
+ "icon_ypos" "2"
+ }
+
+ HudFlashlight
+ {
+ "fieldName" "HudFlashlight"
+ "visible" "1"
+ "PaintBackgroundType" "2"
+ "xpos" "270"
+ "ypos" "444"
+ "ypos" "436"
+ "xpos_hidef" "306" // aligned to left
+ "xpos_lodef" "c-18" // centered in screen
+ "ypos" "428"
+ "tall" "24"
+ "tall" "30"
+ "wide" "36"
+ "wide" "46"
+ "font" "WeaponIconsSmall"
+ "font" "FlashlightDeck"
+
+ "icon_xpos" "4"
+ "icon_ypos" "-8"
+ "icon_ypos" "-12"
+
+ "BarInsetX" "4"
+ "BarInsetY" "18"
+ "BarInsetY" "22"
+ "BarWidth" "28"
+ "BarWidth" "36"
+ "BarHeight" "2"
+ "BarChunkWidth" "2"
+ "BarHeight" "3"
+ "BarChunkWidth" "3"
+ "BarChunkGap" "1"
+ }
+ HudDamageIndicator
+ {
+ "fieldName" "HudDamageIndicator"
+ "visible" "1"
+ "enabled" "1"
+ "DmgColorLeft" "255 0 0 0"
+ "DmgColorRight" "255 0 0 0"
+
+ "dmg_xpos" "30"
+ "dmg_ypos" "100"
+ "dmg_wide" "36"
+ "dmg_tall1" "240"
+ "dmg_tall2" "200"
+ }
+
+ HudZoom
+ {
+ "fieldName" "HudZoom"
+ "visible" "1"
+ "enabled" "1"
+ "Circle1Radius" "66"
+ "Circle2Radius" "74"
+ "DashGap" "16"
+ "DashHeight" "4"
+ "DashHeight" "6"
+ "BorderThickness" "88"
+ }
+ HudWeaponSelection
+ {
+ "fieldName" "HudWeaponSelection"
+ "ypos" "16"
+ "ypos" "32"
+ "visible" "1"
+ "enabled" "1"
+ "SmallBoxSize" "32"
+ "MediumBoxWide" "95"
+ "MediumBoxWide_hidef" "78"
+ "MediumBoxTall" "50"
+ "MediumBoxTall_hidef" "50"
+ "MediumBoxWide_lodef" "74"
+ "MediumBoxTall_lodef" "50"
+ "LargeBoxWide" "112"
+ "LargeBoxTall" "84"
+ "BoxGap" "8"
+ "SelectionNumberXPos" "4"
+ "SelectionNumberYPos" "4"
+ "SelectionGrowTime" "0.4"
+ "TextYPos" "64"
+ }
+
+ HudCrosshair
+ {
+ "fieldName" "HudCrosshair"
+ "visible" "1"
+ "enabled" "1"
+ "wide" "640"
+ "tall" "480"
+ }
+
+ HudDeathNotice
+ {
+ "fieldName" "HudDeathNotice"
+ "visible" "1"
+ "enabled" "1"
+ "wide" "640"
+ "tall" "480"
+ }
+
+ HudVehicle
+ {
+ "fieldName" "HudVehicle"
+ "visible" "1"
+ "enabled" "1"
+ "wide" "640"
+ "tall" "480"
+ }
+
+ ScorePanel
+ {
+ "fieldName" "ScorePanel"
+ "visible" "1"
+ "enabled" "1"
+ "wide" "640"
+ "tall" "480"
+ }
+
+ HudTrain
+ {
+ "fieldName" "HudTrain"
+ "visible" "1"
+ "enabled" "1"
+ "wide" "640"
+ "tall" "480"
+ }
+
+ HudMOTD
+ {
+ "fieldName" "HudMOTD"
+ "visible" "1"
+ "enabled" "1"
+ "wide" "640"
+ "tall" "480"
+ }
+
+ HudMessage
+ {
+ "fieldName" "HudMessage"
+ "visible" "1"
+ "enabled" "1"
+ "wide" "f0"
+ "tall" "480"
+ }
+
+ HudMenu
+ {
+ "fieldName" "HudMenu"
+ "visible" "1"
+ "enabled" "1"
+ "wide" "640"
+ "tall" "480"
+ }
+
+ HudCloseCaption
+ {
+ "fieldName" "HudCloseCaption"
+ "visible" "1"
+ "enabled" "1"
+ "xpos" "c-250"
+ "ypos" "276"
+ "ypos" "236"
+ "wide" "500"
+ "tall" "136"
+ "tall" "176"
+
+ "BgAlpha" "128"
+
+ "GrowTime" "0.25"
+ "ItemHiddenTime" "0.2" // Nearly same as grow time so that the item doesn't start to show until growth is finished
+ "ItemFadeInTime" "0.15" // Once ItemHiddenTime is finished, takes this much longer to fade in
+ "ItemFadeOutTime" "0.3"
+ "topoffset" "0"
+ "topoffset" "0"
+ }
+
+ HudChat
+ {
+ "fieldName" "HudChat"
+ "visible" "0"
+ "enabled" "1"
+ "xpos" "0"
+ "ypos" "0"
+ "wide" "4"
+ "tall" "4"
+ }
+
+ HudHistoryResource
+ {
+ "fieldName" "HudHistoryResource"
+ "visible" "1"
+ "enabled" "1"
+ "xpos" "r272"
+ "xpos" "r252"
+ "ypos" "40"
+ "wide" "248"
+ "tall" "320"
+
+ "history_gap" "64"
+ "history_gap" "56"
+ "icon_inset" "38"
+ "text_inset" "36"
+ "text_inset" "26"
+ "NumberFont" "HudNumbersSmall"
+ }
+
+ HudGeiger
+ {
+ "fieldName" "HudGeiger"
+ "visible" "1"
+ "enabled" "1"
+ "wide" "640"
+ "tall" "480"
+ }
+
+ HUDQuickInfo
+ {
+ "fieldName" "HUDQuickInfo"
+ "visible" "1"
+ "enabled" "1"
+ "wide" "640"
+ "tall" "480"
+ }
+
+ HudWeapon
+ {
+ "fieldName" "HudWeapon"
+ "visible" "1"
+ "enabled" "1"
+ "wide" "640"
+ "tall" "480"
+ }
+ HudAnimationInfo
+ {
+ "fieldName" "HudAnimationInfo"
+ "visible" "1"
+ "enabled" "1"
+ "wide" "640"
+ "tall" "480"
+ }
+
+ HudPredictionDump
+ {
+ "fieldName" "HudPredictionDump"
+ "visible" "1"
+ "enabled" "1"
+ "wide" "3840"
+ "tall" "1080"
+ }
+
+ HudHintDisplay
+ {
+ "fieldName" "HudHintDisplay"
+ "visible" "0"
+ "enabled" "1"
+ "xpos" "c-240"
+ "ypos" "c60"
+ "xpos" "r148"
+ "ypos" "r338"
+ "wide" "480"
+ "tall" "100"
+ "HintSize" "1"
+ "text_xpos" "8"
+ "text_ypos" "8"
+ "center_x" "0" // center text horizontally
+ "center_y" "-1" // align text on the bottom
+ "paintbackground" "0"
+ }
+
+ HudHintKeyDisplay
+ {
+ "fieldName" "HudHintKeyDisplay"
+ "visible" "0"
+ "enabled" "1"
+ "xpos" "r120"
+ "ypos" "r340"
+ "xpos" "r148"
+ "ypos" "r338"
+ "wide" "100"
+ "tall" "200"
+ "text_xpos" "8"
+ "text_ypos" "8"
+ "text_xgap" "8"
+ "text_ygap" "8"
+ "TextColor" "255 170 0 220"
+
+ "PaintBackgroundType" "2"
+ }
+
+
+ HudSquadStatus
+ {
+ "fieldName" "HudSquadStatus"
+ "visible" "1"
+ "enabled" "1"
+ "xpos" "r120"
+ "ypos" "380"
+ "wide" "104"
+ "tall" "46"
+ "text_xpos" "8"
+ "text_ypos" "34"
+ "SquadIconColor" "255 220 0 160"
+ "IconInsetX" "8"
+ "IconInsetY" "0"
+ "IconGap" "24"
+
+ "PaintBackgroundType" "2"
+ }
+ HudSquadStatus
+ {
+ "fieldName" "HudSquadStatus"
+ "visible" "1"
+ "enabled" "1"
+ "xpos" "r160"
+ "ypos" "372"
+ "wide" "144"
+ "tall" "46"
+ "text_xpos" "8"
+ "text_ypos" "28"
+ "SquadIconColor" "255 220 0 160"
+ "IconInsetX" "8"
+ "IconInsetY" "-10"
+ "IconGap" "39"
+
+ "PaintBackgroundType" "2"
+ }
+
+
+ HudPoisonDamageIndicator
+ {
+ "fieldName" "HudPoisonDamageIndicator"
+ "visible" "0"
+ "enabled" "1"
+ "xpos" "16"
+ "ypos" "338"
+ "wide" "136"
+ "tall" "38"
+ "text_xpos" "8"
+ "text_ypos" "8"
+ "text_ygap" "14"
+ "TextColor" "255 170 0 220"
+ "PaintBackgroundType" "2"
+ }
+ HudPoisonDamageIndicator
+ {
+ "fieldName" "HudPoisonDamageIndicator"
+ "visible" "0"
+ "enabled" "1"
+ "xpos" "16"
+ "ypos" "262"
+ "wide" "190"
+ "tall" "42"
+ "text_xpos" "8"
+ "text_ypos" "8"
+ "text_ygap" "14"
+ "TextColor" "255 170 0 220"
+ "PaintBackgroundType" "2"
+ }
+ HudCredits
+ {
+ "fieldName" "HudCredits"
+ "TextFont" "Default"
+ "visible" "1"
+ "xpos" "0"
+ "ypos" "0"
+ "wide" "640"
+ "tall" "480"
+ "TextColor" "255 255 255 192"
+
+ }
+
+ HUDAutoAim
+ {
+ "fieldName" "HUDAutoAim"
+ "visible" "1"
+ "enabled" "1"
+ "wide" "640"
+ "tall" "480"
+ "wide" "960"
+ "tall" "720"
+ }
+
+ HudCommentary
+ {
+ "fieldName" "HudCommentary"
+ "xpos" "c-190"
+ "ypos" "350"
+ "wide" "380"
+ "tall" "40"
+ "visible" "1"
+ "enabled" "1"
+
+ "PaintBackgroundType" "2"
+
+ "bar_xpos" "50"
+ "bar_ypos" "20"
+ "bar_height" "8"
+ "bar_width" "320"
+ "speaker_xpos" "50"
+ "speaker_ypos" "8"
+ "count_xpos_from_right" "10" // Counts from the right side
+ "count_ypos" "8"
+
+ "icon_texture" "vgui/hud/icon_commentary"
+ "icon_xpos" "0"
+ "icon_ypos" "0"
+ "icon_width" "40"
+ "icon_height" "40"
+ }
+
+ HudHDRDemo
+ {
+ "fieldName" "HudHDRDemo"
+ "xpos" "0"
+ "ypos" "0"
+ "wide" "640"
+ "tall" "480"
+ "visible" "1"
+ "enabled" "1"
+
+ "Alpha" "255"
+ "PaintBackgroundType" "2"
+
+ "BorderColor" "0 0 0 255"
+ "BorderLeft" "16"
+ "BorderRight" "16"
+ "BorderTop" "16"
+ "BorderBottom" "64"
+ "BorderCenter" "0"
+
+ "TextColor" "255 255 255 255"
+ "LeftTitleY" "422"
+ "RightTitleY" "422"
+ }
+
+ AchievementNotificationPanel
+ {
+ "fieldName" "AchievementNotificationPanel"
+ "visible" "1"
+ "enabled" "1"
+ "xpos" "0"
+ "ypos" "180"
+ "wide" "f10"
+ "wide" "f60"
+ "tall" "100"
+ }
+
+ CHudVote
+ {
+ "fieldName" "CHudVote"
+ "xpos" "0"
+ "ypos" "0"
+ "wide" "640"
+ "tall" "480"
+ "visible" "1"
+ "enabled" "1"
+ "bgcolor_override" "0 0 0 0"
+ "PaintBackgroundType" "0" // rounded corners
+ }
+}
\ No newline at end of file
\ No newline at end of file
@@ -1,4 +1,3 @@
@@ -1,4 +1,3 @@
-soundscaples_manifest
+"scripts/soundscapes_manifest.txt"
{
{
- "file" "scripts/soundscapes_openvibe.txt"
}
}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+// OpenVibe: Source intentionally leaves the generic mapspawn hook empty.
new file mode 100644
new file mode 100644
@@ -0,0 +1,68 @@
@@ -0,0 +1,68 @@
+// 357
+
+WeaponData
+{
+ // Weapon data is loaded by both the Game and Client DLLs.
+ "printname" "#HL2_357Handgun"
+ "viewmodel" "models/weapons/v_357.mdl"
+ "playermodel" "models/weapons/w_357.mdl"
+ "anim_prefix" "python"
+ "bucket" "1"
+ "bucket_position" "1"
+ "bucket_360" "0"
+ "bucket_position_360" "1"
+
+ "clip_size" "6"
+ "default_clip" "6"
+ "primary_ammo" "357"
+ "secondary_ammo" "None"
+
+ "weight" "7"
+ "rumble" "2"
+ "item_flags" "0"
+
+ // Sounds for the weapon. There is a max of 16 sounds per category (i.e. max 16 "single_shot" sounds)
+ SoundData
+ {
+ "empty" "Weapon_Pistol.Empty"
+ "single_shot" "Weapon_357.Single"
+ }
+
+ // Weapon Sprite data is loaded by the Client DLL.
+ TextureData
+ {
+ "weapon"
+ {
+ "font" "WeaponIcons"
+ "character" "e"
+ }
+ "weapon_s"
+ {
+ "font" "WeaponIconsSelected"
+ "character" "e"
+ }
+ "weapon_small"
+ {
+ "font" "WeaponIconsSmall"
+ "character" "e"
+ }
+ "ammo"
+ {
+ "font" "WeaponIconsSmall"
+ "character" "q"
+ }
+ "crosshair"
+ {
+ "font" "Crosshairs"
+ "character" "Q"
+ }
+ "autoaim"
+ {
+ "file" "sprites/crosshairs"
+ "x" "0"
+ "y" "48"
+ "width" "24"
+ "height" "24"
+ }
+ }
+}
\ No newline at end of file
\ No newline at end of file
new file mode 100644
new file mode 100644
@@ -0,0 +1,85 @@
@@ -0,0 +1,85 @@
+// Assault Rifle 2
+
+WeaponData
+{
+ // Weapon data is loaded by both the Game and Client DLLs.
+ "printname" "#HL2_Pulse_Rifle"
+ "viewmodel" "models/weapons/v_irifle.mdl"
+ "playermodel" "models/weapons/w_irifle.mdl"
+ "anim_prefix" "ar2"
+ "bucket" "2"
+ "bucket_position" "1"
+ "bucket_360" "1"
+ "bucket_position_360" "2"
+
+ "clip_size" "30"
+ "default_clip" "30"
+
+ "clip2_size" "-1"
+ "default_clip2" "-1"
+
+ "primary_ammo" "AR2"
+ "secondary_ammo" "AR2AltFire"
+
+ "weight" "5"
+ "rumble" "4"
+ "item_flags" "0"
+
+ // Sounds for the weapon. There is a max of 16 sounds per category (i.e. max 16 "single_shot" sounds)
+ SoundData
+ {
+ "special1" "Weapon_CombineGuard.Special1"
+ "empty" "Weapon_IRifle.Empty"
+ "double_shot" "Weapon_IRifle.Single"
+ "reload" "Weapon_AR2.Reload"
+ "single_shot" "Weapon_AR2.Single"
+
+ // NPC SECTION
+ "single_shot_npc" "Weapon_AR2.NPC_Single"
+ "reload_npc" "Weapon_AR2.NPC_Reload"
+ "double_shot_npc" "Weapon_AR2.NPC_Double"
+ }
+
+ // Weapon Sprite data is loaded by the Client DLL.
+ TextureData
+ {
+ "weapon"
+ {
+ "font" "WeaponIcons"
+ "character" "l"
+ }
+ "weapon_s"
+ {
+ "font" "WeaponIconsSelected"
+ "character" "l"
+ }
+ "weapon_small"
+ {
+ "font" "WeaponIconsSmall"
+ "character" "l"
+ }
+ "ammo"
+ {
+ "font" "WeaponIconsSmall"
+ "character" "u"
+ }
+ "ammo2"
+ {
+ "font" "WeaponIconsSmall"
+ "character" "z"
+ }
+ "crosshair"
+ {
+ "font" "Crosshairs"
+ "character" "Q"
+ }
+ "autoaim"
+ {
+ "file" "sprites/crosshairs"
+ "x" "0"
+ "y" "48"
+ "width" "24"
+ "height" "24"
+ }
+ }
+}
\ No newline at end of file
\ No newline at end of file
new file mode 100644
new file mode 100644
@@ -0,0 +1,82 @@
@@ -0,0 +1,82 @@
+// Crossbow
+
+WeaponData
+{
+ // Weapon data is loaded by both the Game and Client DLLs.
+ "printname" "#HL2_Crossbow"
+ "viewmodel" "models/weapons/v_crossbow.mdl"
+ "playermodel" "models/weapons/w_crossbow.mdl"
+ "anim_prefix" "bow"
+ "bucket" "3"
+ "bucket_position" "1"
+ "bucket_360" "0"
+ "bucket_position_360" "2"
+
+ "clip_size" "1"
+ "default_clip" "5"
+ "primary_ammo" "XBowBolt"
+ "secondary_ammo" "None"
+
+ "weight" "6"
+ "item_flags" "0"
+
+ // Sounds for the weapon. There is a max of 16 sounds per category (i.e. max 16 "single_shot" sounds)
+ SoundData
+ {
+ "single_shot" "Weapon_Crossbow.Single"
+ "reload" "Weapon_Crossbow.Reload"
+ "special1" "Weapon_Crossbow.BoltElectrify"
+ "special2" "Weapon_Crossbow.BoltFly"
+ }
+
+ // Weapon Sprite data is loaded by the Client DLL.
+ TextureData
+ {
+ "weapon"
+ {
+ "font" "WeaponIcons"
+ "character" "g"
+ }
+ "weapon_s"
+ {
+ "font" "WeaponIconsSelected"
+ "character" "g"
+ }
+ "weapon_small"
+ {
+ "font" "WeaponIconsSmall"
+ "character" "g"
+ }
+ "ammo"
+ {
+ "font" "WeaponIconsSmall"
+ "character" "w"
+ }
+ "crosshair"
+ {
+ "font" "Crosshairs"
+ "character" "Q"
+ }
+ "autoaim"
+ {
+ "file" "sprites/crosshairs"
+ "x" "0"
+ "y" "96"
+ "width" "24"
+ "height" "24"
+ }
+ "zoom"
+ {
+ "font" "Crosshairs"
+ "character" "Q"
+ }
+ "zoom_autoaim"
+ {
+ "file" "sprites/crosshairs"
+ "x" "24"
+ "y" "112"
+ "width" "104"
+ "height" "16"
+ }
+ }
+}
\ No newline at end of file
\ No newline at end of file
new file mode 100644
new file mode 100644
@@ -0,0 +1,62 @@
@@ -0,0 +1,62 @@
+// Crowbar
+
+WeaponData
+{
+ // Weapon data is loaded by both the Game and Client DLLs.
+ "printname" "#HL2_Crowbar"
+ "viewmodel" "models/weapons/v_crowbar.mdl"
+ "playermodel" "models/weapons/w_crowbar.mdl"
+ "anim_prefix" "crowbar"
+ "bucket" "0"
+ "bucket_position" "0"
+ "bucket_360" "2"
+ "bucket_position_360" "0"
+
+ "clip_size" "-1"
+ "primary_ammo" "None"
+ "secondary_ammo" "None"
+
+ "weight" "0"
+ "item_flags" "0"
+
+ // Sounds for the weapon. There is a max of 16 sounds per category (i.e. max 16 "single_shot" sounds)
+ SoundData
+ {
+ "single_shot" "Weapon_Crowbar.Single"
+ "melee_hit" "Weapon_Crowbar.Melee_Hit"
+ "melee_hit_world" "Weapon_Crowbar.Melee_HitWorld"
+ }
+
+ // Weapon Sprite data is loaded by the Client DLL.
+ TextureData
+ {
+ "weapon"
+ {
+ "font" "WeaponIcons"
+ "character" "c"
+ }
+ "weapon_s"
+ {
+ "font" "WeaponIconsSelected"
+ "character" "c"
+ }
+ "ammo"
+ {
+ "font" "WeaponIcons"
+ "character" "c"
+ }
+ "crosshair"
+ {
+ "font" "Crosshairs"
+ "character" "Q"
+ }
+ "autoaim"
+ {
+ "file" "sprites/crosshairs"
+ "x" "0"
+ "y" "48"
+ "width" "24"
+ "height" "24"
+ }
+ }
+}
\ No newline at end of file
\ No newline at end of file
new file mode 100644
new file mode 100644
@@ -0,0 +1,69 @@
@@ -0,0 +1,69 @@
+WeaponData
+{
+ // Weapon data is loaded by both the Game and Client DLLs.
+ "printname" "#HL2_Grenade"
+ "viewmodel" "models/weapons/v_grenade.mdl"
+ "playermodel" "models/items/grenadeAmmo.mdl"
+ "anim_prefix" "Grenade"
+ "bucket" "4"
+ "bucket_position" "0"
+ "bucket_360" "3"
+ "bucket_position_360" "0"
+
+ "clip_size" "-1"
+ "clip2_size" "-1"
+ "default_clip" "1"
+ "default_clip2" "-1"
+
+ "primary_ammo" "grenade"
+ "secondary_ammo" "None"
+
+ "weight" "1"
+ "item_flags" "18" // ITEM_FLAG_NOAUTORELOAD | ITEM_FLAG_EXHAUSTIBLE
+
+ // Sounds for the weapon. There is a max of 16 sounds per category (i.e. max 16 "single_shot" sounds)
+ SoundData
+ {
+ "special1" "WeaponFrag.Roll"
+ "double_shot" "common/null.wav"
+ "single_shot" "common/null.wav"
+ }
+
+ // Weapon Sprite data is loaded by the Client DLL.
+ TextureData
+ {
+ "weapon"
+ {
+ "font" "WeaponIcons"
+ "character" "k"
+ }
+ "weapon_s"
+ {
+ "font" "WeaponIconsSelected"
+ "character" "k"
+ }
+ "weapon_small"
+ {
+ "font" "WeaponIconsSmall"
+ "character" "k"
+ }
+ "ammo"
+ {
+ "font" "WeaponIconsSmall"
+ "character" "v"
+ }
+ "crosshair"
+ {
+ "font" "Crosshairs"
+ "character" "Q"
+ }
+ "autoaim"
+ {
+ "file" "sprites/crosshairs"
+ "x" "48"
+ "y" "72"
+ "width" "24"
+ "height" "24"
+ }
+ }
+}
\ No newline at end of file
\ No newline at end of file
new file mode 100644
new file mode 100644
@@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
+weapon_manifest
+{
+ "file" "scripts/weapon_357.txt"
+ "file" "scripts/weapon_alyxgun.txt"
+ "file" "scripts/weapon_annabelle.txt"
+ "file" "scripts/weapon_ar2.txt"
+ "file" "scripts/weapon_bugbait.txt"
+ "file" "scripts/weapon_citizenpackage.txt"
+ "file" "scripts/weapon_citizensuitcase.txt"
+ "file" "scripts/weapon_crossbow.txt"
+ "file" "scripts/weapon_crowbar.txt"
+ "file" "scripts/weapon_cubemap.txt"
+ "file" "scripts/weapon_frag.txt"
+ "file" "scripts/weapon_physcannon.txt"
+ "file" "scripts/weapon_physgun.txt"
+ "file" "scripts/weapon_pistol.txt"
+ "file" "scripts/weapon_rpg.txt"
+ "file" "scripts/weapon_shotgun.txt"
+ "file" "scripts/weapon_smg1.txt"
+ "file" "scripts/weapon_stunstick.txt"
+}
\ No newline at end of file
\ No newline at end of file
new file mode 100644
new file mode 100644
@@ -0,0 +1,72 @@
@@ -0,0 +1,72 @@
+// Physics Gun
+
+WeaponData
+{
+ // Weapon data is loaded by both the Game and Client DLLs.
+ "printname" "#HL2_GravityGun"
+ "viewmodel" "models/weapons/v_physcannon.mdl"
+ "playermodel" "models/weapons/w_Physics.mdl"
+ "anim_prefix" "gauss"
+ "bucket" "0"
+ "bucket_position" "1"
+ "bucket_360" "2"
+ "bucket_position_360" "1"
+ "clip_size" "1"
+ "clip2_size" "0"
+ "primary_ammo" "None"
+ "secondary_ammo" "None"
+ "default_clip" "0"
+ "default_clip2" "0"
+ "weight" "0"
+ "autoswitchto" "0"
+ "autoswitchfrom" "0"
+ "item_flags" "2"
+
+ SoundData
+ {
+ // launch held object
+ "single_shot" "Weapon_PhysCannon.Launch"
+ // Ignore?
+ "reload" "Weapon_PhysCannon.Charge"
+ // dry fire - no target
+ "empty" "Weapon_PhysCannon.DryFire"
+ // pick up object
+ "special1" "Weapon_PhysCannon.Pickup"
+ // open claws, target object
+ "special2" "Weapon_PhysCannon.OpenClaws"
+ // close claws, target out of range/invalid
+ "melee_hit" "Weapon_PhysCannon.CloseClaws"
+ // drop object
+ "melee_miss" "Weapon_PhysCannon.Drop"
+ // too heavy
+ "special3" "Weapon_PhysCannon.TooHeavy"
+ }
+
+ // Weapon Sprite data is loaded by the Client DLL.
+ TextureData
+ {
+ "weapon"
+ {
+ "font" "WeaponIcons"
+ "character" "m"
+ }
+ "weapon_s"
+ {
+ "font" "WeaponIconsSelected"
+ "character" "m"
+ }
+ "crosshair"
+ {
+ "font" "Crosshairs"
+ "character" "Q"
+ }
+ "autoaim"
+ {
+ "file" "sprites/crosshairs"
+ "x" "48"
+ "y" "72"
+ "width" "24"
+ "height" "24"
+ }
+ }
+}
\ No newline at end of file
\ No newline at end of file
new file mode 100644
new file mode 100644
@@ -0,0 +1,74 @@
@@ -0,0 +1,74 @@
+// Pistol
+
+WeaponData
+{
+ // Weapon data is loaded by both the Game and Client DLLs.
+ "printname" "#HL2_Pistol"
+ "viewmodel" "models/weapons/v_pistol.mdl"
+ "playermodel" "models/weapons/w_pistol.mdl"
+ "anim_prefix" "pistol"
+ "bucket" "1"
+ "bucket_position" "0"
+ "bucket_360" "0"
+ "bucket_position_360" "0"
+
+ "clip_size" "18"
+ "primary_ammo" "Pistol"
+ "secondary_ammo" "None"
+
+ "weight" "2"
+ "rumble" "1"
+ "item_flags" "0"
+
+ // Sounds for the weapon. There is a max of 16 sounds per category (i.e. max 16 "single_shot" sounds)
+ SoundData
+ {
+
+ "reload" "Weapon_Pistol.Reload"
+ "reload_npc" "Weapon_Pistol.NPC_Reload"
+ "empty" "Weapon_Pistol.Empty"
+ "single_shot" "Weapon_Pistol.Single"
+ "single_shot_npc" "Weapon_Pistol.NPC_Single"
+ "special1" "Weapon_Pistol.Special1"
+ "special2" "Weapon_Pistol.Special2"
+ "burst" "Weapon_Pistol.Burst"
+ }
+
+ // Weapon Sprite data is loaded by the Client DLL.
+ TextureData
+ {
+ "weapon"
+ {
+ "font" "WeaponIcons"
+ "character" "d"
+ }
+ "weapon_s"
+ {
+ "font" "WeaponIconsSelected"
+ "character" "d"
+ }
+ "weapon_small"
+ {
+ "font" "WeaponIconsSmall"
+ "character" "d"
+ }
+ "ammo"
+ {
+ "font" "WeaponIconsSmall"
+ "character" "p"
+ }
+ "crosshair"
+ {
+ "font" "Crosshairs"
+ "character" "Q"
+ }
+ "autoaim"
+ {
+ "file" "sprites/crosshairs"
+ "x" "0"
+ "y" "48"
+ "width" "24"
+ "height" "24"
+ }
+ }
+}
\ No newline at end of file
\ No newline at end of file
new file mode 100644
new file mode 100644
@@ -0,0 +1,77 @@
@@ -0,0 +1,77 @@
+// Missile Launcher
+
+WeaponData
+{
+ // Weapon data is loaded by both the Game and Client DLLs.
+ "printname" "#HL2_RPG"
+ "viewmodel" "models/weapons/v_rpg.mdl"
+ "playermodel" "models/weapons/w_rocket_launcher.mdl"
+ "anim_prefix" "missile launcher"
+ "bucket" "4"
+ "bucket_position" "1"
+ "autoswitchfrom" "0"
+ "bucket_360" "3"
+ "bucket_position_360" "1"
+
+ "clip_size" "-1"
+ "clip2_size" "-1"
+ "default_clip" "3"
+ "default_clip2" "-1"
+
+ "primary_ammo" "rpg_round"
+ "secondary_ammo" "None"
+
+ "autoswitchto" "0"
+ "weight" "0"
+ "item_flags" "2"
+
+ // Sounds for the weapon. There is a max of 16 sounds per category (i.e. max 16 "single_shot" sounds)
+ SoundData
+ {
+ "single_shot" "Weapon_RPG.Single"
+ "single_shot_npc" "Weapon_RPG.NPC_Single"
+ //Laser on
+ "special1" "Weapon_RPG.LaserOn"
+ //Laser off
+ "special2" "Weapon_RPG.LaserOff"
+ "empty" "Weapon_SMG1.Empty"
+ }
+
+ // Weapon Sprite data is loaded by the Client DLL.
+ TextureData
+ {
+ "weapon"
+ {
+ "font" "WeaponIcons"
+ "character" "i"
+ }
+ "weapon_s"
+ {
+ "font" "WeaponIconsSelected"
+ "character" "i"
+ }
+ "weapon_small"
+ {
+ "font" "WeaponIconsSmall"
+ "character" "i"
+ }
+ "ammo"
+ {
+ "font" "WeaponIconsSmall"
+ "character" "x"
+ }
+ "crosshair"
+ {
+ "font" "Crosshairs"
+ "character" "Q"
+ }
+ "autoaim"
+ {
+ "file" "sprites/crosshairs"
+ "x" "48"
+ "y" "72"
+ "width" "24"
+ "height" "24"
+ }
+ }
+}
\ No newline at end of file
\ No newline at end of file
new file mode 100644
new file mode 100644
@@ -0,0 +1,74 @@
@@ -0,0 +1,74 @@
+// Shotgun
+
+WeaponData
+{
+ // Weapon data is loaded by both the Game and Client DLLs.
+ "printname" "#HL2_Shotgun"
+ "viewmodel" "models/weapons/v_shotgun.mdl"
+ "playermodel" "models/weapons/w_shotgun.mdl"
+ "anim_prefix" "shotgun"
+ "bucket" "3"
+ "bucket_position" "0"
+ "bucket_360" "1"
+ "bucket_position_360" "1"
+
+ "clip_size" "6"
+ "primary_ammo" "Buckshot"
+ "secondary_ammo" "None"
+
+ "weight" "4"
+ "rumble" "5"
+ "item_flags" "0"
+
+ // Sounds for the weapon. There is a max of 16 sounds per category (i.e. max 16 "single_shot" sounds)
+ SoundData
+ {
+
+ "empty" "Weapon_Shotgun.Empty"
+ "reload" "Weapon_Shotgun.Reload"
+ "special1" "Weapon_Shotgun.Special1"
+ "single_shot" "Weapon_Shotgun.Single"
+ "double_shot" "Weapon_Shotgun.Double"
+ // NPC WEAPON SOUNDS
+ "reload_npc" "Weapon_Shotgun.NPC_Reload"
+ "single_shot_npc" "Weapon_Shotgun.NPC_Single"
+ }
+
+ // Weapon Sprite data is loaded by the Client DLL.
+ TextureData
+ {
+ "weapon"
+ {
+ "font" "WeaponIcons"
+ "character" "b"
+ }
+ "weapon_s"
+ {
+ "font" "WeaponIconsSelected"
+ "character" "b"
+ }
+ "weapon_small"
+ {
+ "font" "WeaponIconsSmall"
+ "character" "b"
+ }
+ "ammo"
+ {
+ "font" "WeaponIconsSmall"
+ "character" "s"
+ }
+ "crosshair"
+ {
+ "font" "Crosshairs"
+ "character" "Q"
+ }
+ "autoaim"
+ {
+ "file" "sprites/crosshairs"
+ "x" "0"
+ "y" "48"
+ "width" "24"
+ "height" "24"
+ }
+ }
+}
\ No newline at end of file
\ No newline at end of file
new file mode 100644
new file mode 100644
@@ -0,0 +1,25 @@
@@ -0,0 +1,25 @@
+WeaponData
+{
+ "printname" "#HL2_SLAM"
+ "viewmodel" "models/weapons/v_slam.mdl"
+ "playermodel" "models/weapons/w_slam.mdl"
+ "anim_prefix" "slam"
+ "bucket" "4"
+ "bucket_position" "1"
+
+ "clip_size" "-1"
+ "clip2_size" "-1"
+ "default_clip" "1"
+ "default_clip2" "-1"
+
+ "primary_ammo" "slam"
+ "secondary_ammo" "None"
+ "weight" "5"
+ "item_flags" "18"
+
+ SoundData
+ {
+ "single_shot" "Weapon_SLAM.SatchelThrow"
+ "special1" "Weapon_SLAM.SatchelDetonate"
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,84 @@
@@ -0,0 +1,84 @@
+// Small Machine Gun 1
+
+WeaponData
+{
+ // Weapon data is loaded by both the Game and Client DLLs.
+ "printname" "#HL2_SMG1"
+ "viewmodel" "models/weapons/v_smg1.mdl"
+ "playermodel" "models/weapons/w_smg1.mdl" //FIXME:
+ "anim_prefix" "smg2"
+ "bucket" "2"
+ "bucket_position" "0"
+ "bucket_360" "1"
+ "bucket_position_360" "0"
+
+ "clip_size" "45"
+ "clip2_size" "-1"
+
+ "default_clip" "45"
+ "default_clip2" "-1"
+
+ "primary_ammo" "SMG1"
+ "secondary_ammo" "SMG1_Grenade"
+
+ "weight" "3"
+ "rumble" "3"
+ "item_flags" "0"
+
+ // Sounds for the weapon. There is a max of 16 sounds per category (i.e. max 16 "single_shot" sounds)
+ SoundData
+ {
+ "reload" "Weapon_SMG1.Reload"
+ "reload_npc" "Weapon_SMG1.NPC_Reload"
+ "empty" "Weapon_SMG1.Empty"
+ "single_shot" "Weapon_SMG1.Single"
+ "single_shot_npc" "Weapon_SMG1.NPC_Single"
+ "special1" "Weapon_SMG1.Special1"
+ "special2" "Weapon_SMG1.Special2"
+ "double_shot" "Weapon_SMG1.Double"
+ "burst" "Weapon_SMG1.Burst"
+ }
+
+ // Weapon Sprite data is loaded by the Client DLL.
+ TextureData
+ {
+ "weapon"
+ {
+ "font" "WeaponIcons"
+ "character" "a"
+ }
+ "weapon_s"
+ {
+ "font" "WeaponIconsSelected"
+ "character" "a"
+ }
+ "weapon_small"
+ {
+ "font" "WeaponIconsSmall"
+ "character" "a"
+ }
+ "ammo"
+ {
+ "font" "WeaponIconsSmall"
+ "character" "r"
+ }
+ "ammo2"
+ {
+ "font" "WeaponIconsSmall"
+ "character" "t"
+ }
+ "crosshair"
+ {
+ "font" "Crosshairs"
+ "character" "Q"
+ }
+ "autoaim"
+ {
+ "file" "sprites/crosshairs"
+ "x" "0"
+ "y" "48"
+ "width" "24"
+ "height" "24"
+ }
+ }
+}
\ No newline at end of file
\ No newline at end of file
new file mode 100644
new file mode 100644
@@ -0,0 +1,65 @@
@@ -0,0 +1,65 @@
+// stunstick
+
+WeaponData
+{
+ // Weapon data is loaded by both the Game and Client DLLs.
+ "printname" "#HL2_StunBaton"
+ "viewmodel" "models/weapons/v_stunbaton.mdl"
+ "playermodel" "models/weapons/w_stunbaton.mdl"
+ "anim_prefix" "stunbaton"
+ "bucket" "0"
+ "bucket_position" "3"
+
+ "clip_size" "-1"
+ "primary_ammo" "None"
+ "secondary_ammo" "None"
+
+ "weight" "0"
+ "item_flags" "0"
+
+ // Sounds for the weapon. There is a max of 16 sounds per category (i.e. max 16 "single_shot" sounds)
+ SoundData
+ {
+
+ "single_shot" "Weapon_StunStick.Swing"
+ "melee_miss" "Weapon_StunStick.Melee_Miss"
+ "melee_hit" "Weapon_StunStick.Melee_Hit"
+ "melee_hit_world" "Weapon_StunStick.Melee_HitWorld"
+ }
+
+ // Weapon Sprite data is loaded by the Client DLL.
+ TextureData
+ {
+ "weapon"
+ {
+ "font" "WeaponIcons"
+ "character" "n"
+ }
+ "weapon_s"
+ {
+ "font" "WeaponIconsSelected"
+ "character" "n"
+ }
+ "ammo"
+ {
+ "font" "WeaponIcons"
+ "character" "n"
+ }
+ "crosshair"
+ {
+ "file" "sprites/crosshairs"
+ "x" "0"
+ "y" "48"
+ "width" "24"
+ "height" "24"
+ }
+ "autoaim"
+ {
+ "file" "sprites/crosshairs"
+ "x" "48"
+ "y" "72"
+ "width" "24"
+ "height" "24"
+ }
+ }
+}
\ No newline at end of file
\ No newline at end of file
@@ -865,7 +865,7 @@ world
@@ -865,7 +865,7 @@ world
"wait" "1"
"wait" "1"
connections
connections
{
{
- "OnStartTouch" "cmd_return_hub,Command,ov_join hub,0,-1"
+ "OnStartTouch" "cmd_return_hub,Command,connect 127.0.0.1:27015,0,-1"
}
}
solid
solid
{
{
@@ -865,7 +865,7 @@ world
@@ -865,7 +865,7 @@ world
"wait" "1"
"wait" "1"
connections
connections
{
{
- "OnStartTouch" "cmd_return_hub,Command,ov_join hub,0,-1"
+ "OnStartTouch" "cmd_return_hub,Command,connect 127.0.0.1:27015,0,-1"
}
}
solid
solid
{
{
@@ -1026,7 +1026,6 @@ world
@@ -1026,7 +1026,6 @@ world
"angles" "0 0 0"
"angles" "0 0 0"
"model" "models/Humans/Group03/male_07.mdl"
"model" "models/Humans/Group03/male_07.mdl"
"solid" "0"
"solid" "0"
- "DefaultAnim" "idle_all_01"
}
}
entity
entity
{
{
@@ -1037,7 +1036,6 @@ world
@@ -1037,7 +1036,6 @@ world
"angles" "0 0 0"
"angles" "0 0 0"
"model" "models/Humans/Group03/male_07.mdl"
"model" "models/Humans/Group03/male_07.mdl"
"solid" "0"
"solid" "0"
- "DefaultAnim" "idle_all_01"
}
}
entity
entity
{
{
@@ -1080,7 +1078,7 @@ world
@@ -1080,7 +1078,7 @@ world
"wait" "1"
"wait" "1"
connections
connections
{
{
- "OnStartTouch" "cmd_join_prophunt,Command,ov_join prophunt,0,-1"
+ "OnStartTouch" "cmd_join_prophunt,Command,connect 127.0.0.2:27016,0,-1"
}
}
solid
solid
{
{
@@ -1178,7 +1176,7 @@ world
@@ -1178,7 +1176,7 @@ world
"wait" "1"
"wait" "1"
connections
connections
{
{
- "OnStartTouch" "cmd_join_deathrun,Command,ov_join deathrun,0,-1"
+ "OnStartTouch" "cmd_join_deathrun,Command,connect 127.0.0.3:27017,0,-1"
}
}
solid
solid
{
{
@@ -1276,7 +1274,7 @@ world
@@ -1276,7 +1274,7 @@ world
"wait" "1"
"wait" "1"
connections
connections
{
{
- "OnStartTouch" "cmd_join_fortwars,Command,ov_join fortwars,0,-1"
+ "OnStartTouch" "cmd_join_fortwars,Command,connect 127.0.0.4:27018,0,-1"
}
}
solid
solid
{
{
@@ -1374,7 +1372,7 @@ world
@@ -1374,7 +1372,7 @@ world
"wait" "1"
"wait" "1"
connections
connections
{
{
- "OnStartTouch" "cmd_join_traitortown,Command,ov_join traitortown,0,-1"
+ "OnStartTouch" "cmd_join_traitortown,Command,connect 127.0.0.5:27019,0,-1"
}
}
solid
solid
{
{
@@ -865,7 +865,7 @@ world
@@ -865,7 +865,7 @@ world
"wait" "1"
"wait" "1"
connections
connections
{
{
- "OnStartTouch" "cmd_return_hub,Command,ov_join hub,0,-1"
+ "OnStartTouch" "cmd_return_hub,Command,connect 127.0.0.1:27015,0,-1"
}
}
solid
solid
{
{
@@ -865,7 +865,7 @@ world
@@ -865,7 +865,7 @@ world
"wait" "1"
"wait" "1"
connections
connections
{
{
- "OnStartTouch" "cmd_return_hub,Command,ov_join hub,0,-1"
+ "OnStartTouch" "cmd_return_hub,Command,connect 127.0.0.1:27015,0,-1"
}
}
solid
solid
{
{
@@ -137,14 +137,12 @@ ipcMain.handle('game:launch', (_e, { ip, port }) => {
@@ -137,14 +137,12 @@ ipcMain.handle('game:launch', (_e, { ip, port }) => {
ipcMain.handle('game:launch-direct', (_e, mode) => {
ipcMain.handle('game:launch-direct', (_e, mode) => {
// Look up the mode's port from local server list
// Look up the mode's port from local server list
- // Use 127.0.1.1 (workstation hostname) instead of 127.0.0.1 since that's where
- // the Source engine binds the server when resolving localhost via /etc/hosts
const serverMap = {
const serverMap = {
- hub: { ip: '127.0.1.1', port: 27015 },
- prophunt: { ip: '127.0.1.1', port: 27016 },
- deathrun: { ip: '127.0.1.1', port: 27017 },
- fortwars: { ip: '127.0.1.1', port: 27018 },
- traitortown: { ip: '127.0.1.1', port: 27019 },
+ hub: { ip: '127.0.0.1', port: 27015 },
+ prophunt: { ip: '127.0.0.2', port: 27016 },
+ deathrun: { ip: '127.0.0.3', port: 27017 },
+ fortwars: { ip: '127.0.0.4', port: 27018 },
+ traitortown: { ip: '127.0.0.5', port: 27019 },
};
};
const srv = serverMap[mode] || serverMap.hub;
const srv = serverMap[mode] || serverMap.hub;
return launchGame(srv.ip, srv.port);
return launchGame(srv.ip, srv.port);
@@ -13,7 +13,7 @@
@@ -13,7 +13,7 @@
"serverSecret": "dev-secret",
"serverSecret": "dev-secret",
"mode": "prophunt",
"mode": "prophunt",
"mapName": "ph_openvibe_dev",
"mapName": "ph_openvibe_dev",
- "publicHost": "127.0.0.1",
+ "publicHost": "127.0.0.2",
"port": 27016,
"port": 27016,
"maxPlayers": 24
"maxPlayers": 24
},
},
@@ -22,7 +22,7 @@
@@ -22,7 +22,7 @@
"serverSecret": "dev-secret",
"serverSecret": "dev-secret",
"mode": "deathrun",
"mode": "deathrun",
"mapName": "dr_openvibe_dev",
"mapName": "dr_openvibe_dev",
- "publicHost": "127.0.0.1",
+ "publicHost": "127.0.0.3",
"port": 27017,
"port": 27017,
"maxPlayers": 24
"maxPlayers": 24
},
},
@@ -31,7 +31,7 @@
@@ -31,7 +31,7 @@
"serverSecret": "dev-secret",
"serverSecret": "dev-secret",
"mode": "fortwars",
"mode": "fortwars",
"mapName": "fw_openvibe_dev",
"mapName": "fw_openvibe_dev",
- "publicHost": "127.0.0.1",
+ "publicHost": "127.0.0.4",
"port": 27018,
"port": 27018,
"maxPlayers": 32
"maxPlayers": 32
},
},
@@ -40,7 +40,7 @@
@@ -40,7 +40,7 @@
"serverSecret": "dev-secret",
"serverSecret": "dev-secret",
"mode": "traitortown",
"mode": "traitortown",
"mapName": "tt_openvibe_dev",
"mapName": "tt_openvibe_dev",
- "publicHost": "127.0.0.1",
+ "publicHost": "127.0.0.5",
"port": 27019,
"port": 27019,
"maxPlayers": 24
"maxPlayers": 24
}
}
new file mode 100755
new file mode 100755
@@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
+OUT_DIR="${OPENVIBE_BACKUP_DIR:-$ROOT/backups}"
+DATABASE_URL="${DATABASE_URL:-postgres://openvibe:[email protected]:5432/openvibe}"
+STAMP="$(date -u +%Y%m%dT%H%M%SZ)"
+OUT="$OUT_DIR/openvibe-$STAMP.sql.gz"
+
+mkdir -p "$OUT_DIR"
+
+if ! command -v pg_dump >/dev/null 2>&1; then
+ echo "pg_dump is required. Install postgresql-client." >&2
+ exit 1
+fi
+
+pg_dump "$DATABASE_URL" | gzip -9 > "$OUT"
+chmod 600 "$OUT"
+
+echo "$OUT"
@@ -25,7 +25,7 @@ done
@@ -25,7 +25,7 @@ done
kill_matching() {
kill_matching() {
local signal="$1"
local signal="$1"
local pattern="$2"
local pattern="$2"
- pgrep -f "$pattern" 2>/dev/null | while read -r pid; do
+ (pgrep -f "$pattern" 2>/dev/null || true) | while read -r pid; do
if [[ "$pid" != "$$" ]]; then
if [[ "$pid" != "$$" ]]; then
kill "-$signal" "$pid" 2>/dev/null || true
kill "-$signal" "$pid" 2>/dev/null || true
fi
fi
@@ -13,6 +13,21 @@ const textures = {
@@ -13,6 +13,21 @@ const textures = {
trigger: "TOOLS/TOOLSTRIGGER",
trigger: "TOOLS/TOOLSTRIGGER",
};
};
+const useAuthenticatedJoin = process.env.OPENVIBE_USE_OV_JOIN === "1";
+
+const localHosts = {
+ hub: "127.0.0.1",
+ prophunt: "127.0.0.2",
+ deathrun: "127.0.0.3",
+ fortwars: "127.0.0.4",
+ traitortown: "127.0.0.5",
+};
+
+function joinCommand(mode, port) {
+ if (useAuthenticatedJoin) return `ov_join ${mode}`;
+ return `connect ${localHosts[mode] ?? localHosts.hub}:${port}`;
+}
+
function id() {
function id() {
return nextId++;
return nextId++;
}
}
@@ -197,7 +212,6 @@ function propNpc(name, origin, angle = 180) {
@@ -197,7 +212,6 @@ function propNpc(name, origin, angle = 180) {
angles: `0 ${angle} 0`,
angles: `0 ${angle} 0`,
model: "models/Humans/Group03/male_07.mdl",
model: "models/Humans/Group03/male_07.mdl",
solid: "0",
solid: "0",
- DefaultAnim: "idle_all_01",
});
});
}
}
@@ -348,7 +362,7 @@ function hub() {
@@ -348,7 +362,7 @@ function hub() {
[pad.origin[0] - 128, pad.origin[1] - 128, 16],
[pad.origin[0] - 128, pad.origin[1] - 128, 16],
[pad.origin[0] + 128, pad.origin[1] + 128, 128],
[pad.origin[0] + 128, pad.origin[1] + 128, 128],
commandTarget,
commandTarget,
- `ov_join ${pad.mode}`,
+ joinCommand(pad.mode, pad.port),
),
),
];
];
}),
}),
@@ -417,7 +431,7 @@ function minigame(mapName, modeName, returnX = 0) {
@@ -417,7 +431,7 @@ function minigame(mapName, modeName, returnX = 0) {
[returnX - 128, 580, 16],
[returnX - 128, 580, 16],
[returnX + 128, 836, 128],
[returnX + 128, 836, 128],
"cmd_return_hub",
"cmd_return_hub",
- "ov_join hub",
+ joinCommand("hub", 27015),
),
),
];
];
@@ -45,4 +45,5 @@ exec "$GE_PROTON/proton" waitforexitandrun \
@@ -45,4 +45,5 @@ exec "$GE_PROTON/proton" waitforexitandrun \
"$HL2_EXE" \
"$HL2_EXE" \
-game "$GAME_DIR" \
-game "$GAME_DIR" \
-console -dev -novid -sw -w 1280 -h 720 \
-console -dev -novid -sw -w 1280 -h 720 \
+ -port 27115 -clientport 27105 \
$CONNECT_ARGS
$CONNECT_ARGS
@@ -26,4 +26,6 @@ exec "$LAUNCHER" \
@@ -26,4 +26,6 @@ exec "$LAUNCHER" \
-dev \
-dev \
-sw \
-sw \
-w 1280 \
-w 1280 \
- -h 720
+ -h 720 \
+ -port 27115 \
+ -clientport 27105
@@ -17,6 +17,17 @@ SRCDS="${OPENVIBE_SRCDS:-$HOME/srcds/tf2}"
@@ -17,6 +17,17 @@ SRCDS="${OPENVIBE_SRCDS:-$HOME/srcds/tf2}"
MOD="$ROOT/game/openvibe.games"
MOD="$ROOT/game/openvibe.games"
MAP_DELAY="${OPENVIBE_SRCDS_MAP_DELAY:-6}"
MAP_DELAY="${OPENVIBE_SRCDS_MAP_DELAY:-6}"
+case "$MODE" in
+ hub) DEFAULT_BIND_IP="127.0.0.1" ;;
+ prophunt) DEFAULT_BIND_IP="127.0.0.2" ;;
+ deathrun) DEFAULT_BIND_IP="127.0.0.3" ;;
+ fortwars) DEFAULT_BIND_IP="127.0.0.4" ;;
+ traitortown) DEFAULT_BIND_IP="127.0.0.5" ;;
+ *) DEFAULT_BIND_IP="127.0.0.1" ;;
+esac
+
+BIND_IP="${OPENVIBE_BIND_IP:-$DEFAULT_BIND_IP}"
+
if [[ ! -x "$SRCDS/srcds_linux64" ]]; then
if [[ ! -x "$SRCDS/srcds_linux64" ]]; then
echo "Missing 64-bit SRCDS at $SRCDS/srcds_linux64" >&2
echo "Missing 64-bit SRCDS at $SRCDS/srcds_linux64" >&2
echo "Install Team Fortress 2 Dedicated Server AppID 232250 into $SRCDS." >&2
echo "Install Team Fortress 2 Dedicated Server AppID 232250 into $SRCDS." >&2
@@ -33,17 +44,28 @@ cd "$SRCDS"
@@ -33,17 +44,28 @@ cd "$SRCDS"
printf '243750\n' > "$SRCDS/steam_appid.txt"
printf '243750\n' > "$SRCDS/steam_appid.txt"
-export LD_LIBRARY_PATH="$MOD/bin/linux64:.:$SRCDS/bin/linux64:$SRCDS/bin:${LD_LIBRARY_PATH:-}"
+COMPAT_LIB_DIR="${OPENVIBE_SRCDS_COMPAT_LIB_DIR:-/tmp/openvibe-srcds-compat-$USER}"
+if [[ ! -e /lib/i386-linux-gnu/libtinfo.so.5 && -e /lib/i386-linux-gnu/libtinfo.so.6 ]]; then
+ mkdir -p "$COMPAT_LIB_DIR"
+ ln -sfn /lib/i386-linux-gnu/libtinfo.so.6 "$COMPAT_LIB_DIR/libtinfo.so.5"
+fi
+if [[ ! -e /lib/i386-linux-gnu/libncurses.so.5 && -e /lib/i386-linux-gnu/libncurses.so.6 ]]; then
+ mkdir -p "$COMPAT_LIB_DIR"
+ ln -sfn /lib/i386-linux-gnu/libncurses.so.6 "$COMPAT_LIB_DIR/libncurses.so.5"
+fi
+
+export LD_LIBRARY_PATH="$COMPAT_LIB_DIR:$MOD/bin/linux64:.:$SRCDS/bin/linux64:$SRCDS/bin:${LD_LIBRARY_PATH:-}"
server_cmd=(
server_cmd=(
./srcds_linux64
./srcds_linux64
-game "$MOD"
-game "$MOD"
-console
-console
-usercon
-usercon
- -condebug
- +ip 0.0.0.0
- +port "$PORT"
+ -ip "$BIND_IP"
+ -port "$PORT"
+ +clientport "$((PORT + 1000))"
+maxplayers "$MAXPLAYERS"
+maxplayers "$MAXPLAYERS"
+ +tv_port "$((PORT + 2000))"
+exec "$CFG"
+exec "$CFG"
)
)
@@ -54,11 +76,16 @@ if [[ "${OPENVIBE_SRCDS_PIPE_MAP:-1}" == "1" ]]; then
@@ -54,11 +76,16 @@ if [[ "${OPENVIBE_SRCDS_PIPE_MAP:-1}" == "1" ]]; then
printf 'map %s\n' "$MAP"
printf 'map %s\n' "$MAP"
while sleep 3600; do :; done
while sleep 3600; do :; done
}
}
+ filter_output() {
+ sed -u \
+ -e '/WARNING: Failed to load 32-bit libtinfo\.so\.5 or libncurses\.so\.5\./d' \
+ -e '/Please install (lib32tinfo5 \/ ncurses-libs\.i686 \/ equivalent) to enable readline\./d'
+ }
if command -v script >/dev/null 2>&1; then
if command -v script >/dev/null 2>&1; then
- map_input | script -qfec "$server_command" /dev/null
+ map_input | script -qfec "$server_command" /dev/null | filter_output
else
else
- map_input | "${server_cmd[@]}"
+ map_input | "${server_cmd[@]}" | filter_output
fi
fi
else
else
exec "${server_cmd[@]}" +map "$MAP"
exec "${server_cmd[@]}" +map "$MAP"
@@ -25,12 +25,21 @@ if [[ -d "$ROOT/game/openvibe.games/logs" ]]; then
@@ -25,12 +25,21 @@ if [[ -d "$ROOT/game/openvibe.games/logs" ]]; then
LOG_DIR="$ROOT/game/openvibe.games/logs"
LOG_DIR="$ROOT/game/openvibe.games/logs"
fi
fi
+case "$MODE" in
+ hub) DEFAULT_PUBLIC_HOST="127.0.0.1" ;;
+ prophunt) DEFAULT_PUBLIC_HOST="127.0.0.2" ;;
+ deathrun) DEFAULT_PUBLIC_HOST="127.0.0.3" ;;
+ fortwars) DEFAULT_PUBLIC_HOST="127.0.0.4" ;;
+ traitortown) DEFAULT_PUBLIC_HOST="127.0.0.5" ;;
+ *) DEFAULT_PUBLIC_HOST="127.0.0.1" ;;
+esac
+
exec node "$ROOT/tools/ov-sidecar.mjs" \
exec node "$ROOT/tools/ov-sidecar.mjs" \
--server-id "$SERVER_ID" \
--server-id "$SERVER_ID" \
--server-secret "${OPENVIBE_SERVER_SECRET:-dev-secret}" \
--server-secret "${OPENVIBE_SERVER_SECRET:-dev-secret}" \
--mode "$MODE" \
--mode "$MODE" \
--port "$PORT" \
--port "$PORT" \
--max-players "$MAX_PLAYERS" \
--max-players "$MAX_PLAYERS" \
- --host "${OPENVIBE_PUBLIC_HOST:-127.0.0.1}" \
+ --host "${OPENVIBE_PUBLIC_HOST:-$DEFAULT_PUBLIC_HOST}" \
--api-url "${OPENVIBE_API_URL:-http://127.0.0.1:3000}" \
--api-url "${OPENVIBE_API_URL:-http://127.0.0.1:3000}" \
--log-dir "$LOG_DIR"
--log-dir "$LOG_DIR"
@@ -38,7 +38,7 @@ const server = await post("/v1/servers/register", {
@@ -38,7 +38,7 @@ const server = await post("/v1/servers/register", {
serverSecret,
serverSecret,
mode: "prophunt",
mode: "prophunt",
mapName: "ph_openvibe_dev",
mapName: "ph_openvibe_dev",
- publicHost: "127.0.0.1",
+ publicHost: "127.0.0.2",
port: 27016,
port: 27016,
maxPlayers: 24,
maxPlayers: 24,
});
});
@@ -11,6 +11,8 @@ run_check() {
@@ -11,6 +11,8 @@ run_check() {
local map="$3"
local map="$3"
local log="/tmp/openvibe-srcds-$name.log"
local log="/tmp/openvibe-srcds-$name.log"
+ rm -f "$log"
+
set +e
set +e
timeout "${OPENVIBE_SRCDS_SMOKE_TIMEOUT:-70}s" "$ROOT/tools/$script" >"$log" 2>&1
timeout "${OPENVIBE_SRCDS_SMOKE_TIMEOUT:-70}s" "$ROOT/tools/$script" >"$log" 2>&1
local code=$?
local code=$?
@@ -34,7 +36,13 @@ run_check() {
@@ -34,7 +36,13 @@ run_check() {
exit 1
exit 1
fi
fi
- if ! grep -Eq "Started map \"$map\"|Spawn Server: $map|Loading map \"$map\"|SV_ActivateServer" "$log"; then
+ if grep -Eiq "WARNING: Port|Socket bound to non-default|Failed to load 32-bit|CLocalizedStringTable::AddFile|Failed to load custom font|Hud element .*doesn't have an entry|Couldn't parse script sequence|Unknown command|Missing Vgui material|material \".*\" not found" "$log"; then
+ echo "[srcds:$name] reported a startup log warning/error"
+ grep -Ein "WARNING: Port|Socket bound to non-default|Failed to load 32-bit|CLocalizedStringTable::AddFile|Failed to load custom font|Hud element .*doesn't have an entry|Couldn't parse script sequence|Unknown command|Missing Vgui material|material \".*\" not found" "$log"
+ exit 1
+ fi
+
+ if ! grep -Eq "Started map \"$map\"|Spawn Server: $map|Loading map \"$map\"|SV_ActivateServer|ov_.*\.nut ready" "$log"; then
echo "[srcds:$name] did not reach map load for $map"
echo "[srcds:$name] did not reach map load for $map"
tail -120 "$log"
tail -120 "$log"
exit 1
exit 1