openvibers/source.openvibe.games Commits

Clean OpenVibe startup and add hardening APIs

CO Copilot committed 29 Jun 2026 · d8f4820186 · 69 files, +4795 −80


Changes

ddiff --git a/.gitignore b/.gitignore
iindex 337c366..c7e3701 100644
---- a/.gitignore
++++ b/.gitignore
@@@ -17,8 +17,11 @@ game/openvibe.games/demoheader.tmp
game/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/
*.core
core
ddiff --git a/DEPLOYMENT_STATUS.md b/DEPLOYMENT_STATUS.md
iindex d4ba506..e825e67 100644
---- a/DEPLOYMENT_STATUS.md
++++ b/DEPLOYMENT_STATUS.md
@@@ -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 build
npm test
```
--Result: TypeScript build passed, 9 tests passed.
++Result: TypeScript build passed, 11 tests passed.
```bash
cd ~/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.
ddiff --git a/PROJECT_INDEX.md b/PROJECT_INDEX.md
iindex 6169ce2..33df357 100644
---- a/PROJECT_INDEX.md
++++ b/PROJECT_INDEX.md
@@@ -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.
ddiff --git a/QUICK_REFERENCE.md b/QUICK_REFERENCE.md
iindex 4863c21..d394d35 100644
---- a/QUICK_REFERENCE.md
++++ b/QUICK_REFERENCE.md
@@@ -67,16 +67,18 @@ node tools/generate-dev-vmfs.mjs
tools/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 traitortown
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/assets/manifest | jq
++curl -s http://127.0.0.1:3000/metrics
```
Travel request:
@@@ -128,6 +131,13 @@ OPENVIBE_ADMIN_SECRET
OPENVIBE_CDN_BASE_URL
```
++## Backup
++
++```bash
++cd ~/src/openvibe-source
++tools/backup-postgres.sh
++```
++
## Common Fixes
| Issue | Fix |
ddiff --git a/README.md b/README.md
iindex b947822..65ecca8 100644
---- a/README.md
++++ b/README.md
@@@ -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 shards
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.
++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 Work
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.
- 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.
ddiff --git a/backend/migrations/001_init.sql b/backend/migrations/001_init.sql
iindex 3452d1a..e276bcf 100644
---- a/backend/migrations/001_init.sql
++++ b/backend/migrations/001_init.sql
@@@ -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_open
ON game_servers (mode, state, player_count, last_heartbeat);
@@@ -82,6 +114,12 @@ CREATE INDEX IF NOT EXISTS idx_join_tokens_valid
ON 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),
ddiff --git a/backend/src/app.test.ts b/backend/src/app.test.ts
iindex 5779b8f..bc7ccfc 100644
---- a/backend/src/app.test.ts
++++ b/backend/src/app.test.ts
@@@ -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();
++ });
});
ddiff --git a/backend/src/app.ts b/backend/src/app.ts
iindex 66df5df..cce6288 100644
---- a/backend/src/app.ts
++++ b/backend/src/app.ts
@@@ -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?.();
});
ddiff --git a/backend/src/domain.ts b/backend/src/domain.ts
iindex dd2e7c8..8c53258 100644
---- a/backend/src/domain.ts
++++ b/backend/src/domain.ts
@@@ -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[]>;
}
ddiff --git a/backend/src/repository-memory.ts b/backend/src/repository-memory.ts
iindex 8686402..eebcbb6 100644
---- a/backend/src/repository-memory.ts
++++ b/backend/src/repository-memory.ts
@@@ -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 {
ddiff --git a/backend/src/repository-pg.ts b/backend/src/repository-pg.ts
iindex 30034f7..04f0bbe 100644
---- a/backend/src/repository-pg.ts
++++ b/backend/src/repository-pg.ts
@@@ -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(),
++ };
++}
ddiff --git a/backend/src/schemas.ts b/backend/src/schemas.ts
iindex fb977ba..53070af 100644
---- a/backend/src/schemas.ts
++++ b/backend/src/schemas.ts
@@@ -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),
++});
ddiff --git a/docs/PHASE_2_SUMMARY.md b/docs/PHASE_2_SUMMARY.md
iindex 6669eca..6ea5fff 100644
---- a/docs/PHASE_2_SUMMARY.md
++++ b/docs/PHASE_2_SUMMARY.md
@@@ -69,20 +69,22 @@ npm run build
npm 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
ddiff --git a/docs/architecture.md b/docs/architecture.md
iindex 6761e32..e4432a3 100644
---- a/docs/architecture.md
++++ b/docs/architecture.md
@@@ -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:
```text
1. 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 Hooks
Tracked C++ patch sources live under `sdk/openvibe/` and are copied into the local Valve SDK checkout by `tools/apply-openvibe-sdk.sh`.
ddiff --git a/game/openvibe.games/cfg/autoexec.cfg b/game/openvibe.games/cfg/autoexec.cfg
nnew file mode 100644
iindex 0000000..30acddf
---- /dev/null
++++ b/game/openvibe.games/cfg/autoexec.cfg
@@@ -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."
ddiff --git a/game/openvibe.games/cfg/motd.txt b/game/openvibe.games/cfg/motd.txt
nnew file mode 100644
iindex 0000000..a04599d
---- /dev/null
++++ b/game/openvibe.games/cfg/motd.txt
@@@ -0,0 +1 @@
++Welcome to OpenVibe: Source.
ddiff --git a/game/openvibe.games/cfg/motd_text.txt b/game/openvibe.games/cfg/motd_text.txt
nnew file mode 100644
iindex 0000000..a04599d
---- /dev/null
++++ b/game/openvibe.games/cfg/motd_text.txt
@@@ -0,0 +1 @@
++Welcome to OpenVibe: Source.
ddiff --git a/game/openvibe.games/cfg/pure_server_minimal.txt b/game/openvibe.games/cfg/pure_server_minimal.txt
nnew file mode 100644
iindex 0000000..942c9bc
---- /dev/null
++++ b/game/openvibe.games/cfg/pure_server_minimal.txt
@@@ -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
++}
ddiff --git a/game/openvibe.games/cfg/server.cfg b/game/openvibe.games/cfg/server.cfg
nnew file mode 100644
iindex 0000000..bf07afe
---- /dev/null
++++ b/game/openvibe.games/cfg/server.cfg
@@@ -0,0 +1,2 @@
++// OpenVibe: Source default dedicated server config.
++// Individual shards load openvibe_<mode>.cfg after this file.
ddiff --git a/game/openvibe.games/cfg/trusted_keys_base.txt b/game/openvibe.games/cfg/trusted_keys_base.txt
nnew file mode 100644
iindex 0000000..fb65487
---- /dev/null
++++ b/game/openvibe.games/cfg/trusted_keys_base.txt
@@@ -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"
++ }
++}
ddiff --git a/game/openvibe.games/cfg/valve.rc b/game/openvibe.games/cfg/valve.rc
nnew file mode 100644
iindex 0000000..74bd39e
---- /dev/null
++++ b/game/openvibe.games/cfg/valve.rc
@@@ -0,0 +1,2 @@
++// OpenVibe: Source startup script.
++// Intentionally small; mode-specific server config is passed with +exec.
ddiff --git a/game/openvibe.games/maps/dr_openvibe_dev.bsp b/game/openvibe.games/maps/dr_openvibe_dev.bsp
iindex a711083..c8e07bd 100644
BBinary files a/game/openvibe.games/maps/dr_openvibe_dev.bsp and b/game/openvibe.games/maps/dr_openvibe_dev.bsp differ
ddiff --git a/game/openvibe.games/maps/fw_openvibe_dev.bsp b/game/openvibe.games/maps/fw_openvibe_dev.bsp
iindex e0a43aa..e05e09b 100644
BBinary files a/game/openvibe.games/maps/fw_openvibe_dev.bsp and b/game/openvibe.games/maps/fw_openvibe_dev.bsp differ
ddiff --git a/game/openvibe.games/maps/ov_hub.bsp b/game/openvibe.games/maps/ov_hub.bsp
iindex d6dea0d..fb42222 100644
BBinary files a/game/openvibe.games/maps/ov_hub.bsp and b/game/openvibe.games/maps/ov_hub.bsp differ
ddiff --git a/game/openvibe.games/maps/ph_openvibe_dev.bsp b/game/openvibe.games/maps/ph_openvibe_dev.bsp
iindex 2c08952..1a63c69 100644
BBinary files a/game/openvibe.games/maps/ph_openvibe_dev.bsp and b/game/openvibe.games/maps/ph_openvibe_dev.bsp differ
ddiff --git a/game/openvibe.games/maps/tt_openvibe_dev.bsp b/game/openvibe.games/maps/tt_openvibe_dev.bsp
iindex 5c22909..e4d1cb6 100644
BBinary files a/game/openvibe.games/maps/tt_openvibe_dev.bsp and b/game/openvibe.games/maps/tt_openvibe_dev.bsp differ
ddiff --git a/game/openvibe.games/materials/vgui/gfx/vgui/crosshair.vmt b/game/openvibe.games/materials/vgui/gfx/vgui/crosshair.vmt
nnew file mode 100644
iindex 0000000..27ec5a5
---- /dev/null
++++ b/game/openvibe.games/materials/vgui/gfx/vgui/crosshair.vmt
@@@ -0,0 +1,7 @@
++"UnlitGeneric"
++{
++ "$basetexture" "vgui/white_additive"
++ "$translucent" "1"
++ "$vertexcolor" "1"
++ "$vertexalpha" "1"
++}
ddiff --git a/game/openvibe.games/modelsounds.cache b/game/openvibe.games/modelsounds.cache
iindex 2275a8d..6ab64ae 100644
BBinary files a/game/openvibe.games/modelsounds.cache and b/game/openvibe.games/modelsounds.cache differ
ddiff --git a/game/openvibe.games/resource/ClientScheme.res b/game/openvibe.games/resource/ClientScheme.res
nnew file mode 100644
iindex 0000000..719d4ba
---- /dev/null
++++ b/game/openvibe.games/resource/ClientScheme.res
@@@ -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"
++ }
++
++}
ddiff --git a/game/openvibe.games/resource/GameMenu.res b/game/openvibe.games/resource/GameMenu.res
iindex 0a34088..ec80a9b 100644
---- a/game/openvibe.games/resource/GameMenu.res
++++ b/game/openvibe.games/resource/GameMenu.res
@@@ -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"
ddiff --git a/game/openvibe.games/resource/gordin-black.ttf b/game/openvibe.games/resource/gordin-black.ttf
nnew file mode 100644
iindex 0000000..e6a0ad3
BBinary files /dev/null and b/game/openvibe.games/resource/gordin-black.ttf differ
ddiff --git a/game/openvibe.games/resource/gordin-bold.ttf b/game/openvibe.games/resource/gordin-bold.ttf
nnew file mode 100644
iindex 0000000..7c31eef
BBinary files /dev/null and b/game/openvibe.games/resource/gordin-bold.ttf differ
ddiff --git a/game/openvibe.games/resource/gordin-light.ttf b/game/openvibe.games/resource/gordin-light.ttf
nnew file mode 100644
iindex 0000000..f7d0e0a
BBinary files /dev/null and b/game/openvibe.games/resource/gordin-light.ttf differ
ddiff --git a/game/openvibe.games/resource/gordin-regular.ttf b/game/openvibe.games/resource/gordin-regular.ttf
nnew file mode 100644
iindex 0000000..8a4a195
BBinary files /dev/null and b/game/openvibe.games/resource/gordin-regular.ttf differ
ddiff --git a/game/openvibe.games/resource/gordin-semibold.ttf b/game/openvibe.games/resource/gordin-semibold.ttf
nnew file mode 100644
iindex 0000000..3071e28
BBinary files /dev/null and b/game/openvibe.games/resource/gordin-semibold.ttf differ
ddiff --git a/game/openvibe.games/resource/openvibe.games_english.txt b/game/openvibe.games/resource/openvibe.games_english.txt
nnew file mode 100644
iindex 0000000..bd8c6ac
---- /dev/null
++++ b/game/openvibe.games/resource/openvibe.games_english.txt
@@@ -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."
++ }
++}
ddiff --git a/game/openvibe.games/scripts/HudAnimations.txt b/game/openvibe.games/scripts/HudAnimations.txt
nnew file mode 100644
iindex 0000000..f06eb95
---- /dev/null
++++ b/game/openvibe.games/scripts/HudAnimations.txt
@@@ -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
++}
ddiff --git a/game/openvibe.games/scripts/HudLayout.res b/game/openvibe.games/scripts/HudLayout.res
nnew file mode 100644
iindex 0000000..9888b4d
---- /dev/null
++++ b/game/openvibe.games/scripts/HudLayout.res
@@@ -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
ddiff --git a/game/openvibe.games/scripts/soundscapes_manifest.txt b/game/openvibe.games/scripts/soundscapes_manifest.txt
iindex 43bb3e4..b1b8fc6 100644
---- a/game/openvibe.games/scripts/soundscapes_manifest.txt
++++ b/game/openvibe.games/scripts/soundscapes_manifest.txt
@@@ -1,4 +1,3 @@
--soundscaples_manifest
++"scripts/soundscapes_manifest.txt"
{
-- "file" "scripts/soundscapes_openvibe.txt"
}
ddiff --git a/game/openvibe.games/scripts/vscripts/mapspawn.nut b/game/openvibe.games/scripts/vscripts/mapspawn.nut
nnew file mode 100644
iindex 0000000..b7ed561
---- /dev/null
++++ b/game/openvibe.games/scripts/vscripts/mapspawn.nut
@@@ -0,0 +1 @@
++// OpenVibe: Source intentionally leaves the generic mapspawn hook empty.
ddiff --git a/game/openvibe.games/scripts/weapon_357.txt b/game/openvibe.games/scripts/weapon_357.txt
nnew file mode 100644
iindex 0000000..ddefecb
---- /dev/null
++++ b/game/openvibe.games/scripts/weapon_357.txt
@@@ -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
ddiff --git a/game/openvibe.games/scripts/weapon_ar2.txt b/game/openvibe.games/scripts/weapon_ar2.txt
nnew file mode 100644
iindex 0000000..4f975ab
---- /dev/null
++++ b/game/openvibe.games/scripts/weapon_ar2.txt
@@@ -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
ddiff --git a/game/openvibe.games/scripts/weapon_crossbow.txt b/game/openvibe.games/scripts/weapon_crossbow.txt
nnew file mode 100644
iindex 0000000..72fb53a
---- /dev/null
++++ b/game/openvibe.games/scripts/weapon_crossbow.txt
@@@ -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
ddiff --git a/game/openvibe.games/scripts/weapon_crowbar.txt b/game/openvibe.games/scripts/weapon_crowbar.txt
nnew file mode 100644
iindex 0000000..afad8a0
---- /dev/null
++++ b/game/openvibe.games/scripts/weapon_crowbar.txt
@@@ -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
ddiff --git a/game/openvibe.games/scripts/weapon_frag.txt b/game/openvibe.games/scripts/weapon_frag.txt
nnew file mode 100644
iindex 0000000..351fef8
---- /dev/null
++++ b/game/openvibe.games/scripts/weapon_frag.txt
@@@ -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
ddiff --git a/game/openvibe.games/scripts/weapon_manifest.txt b/game/openvibe.games/scripts/weapon_manifest.txt
nnew file mode 100644
iindex 0000000..bd49e69
---- /dev/null
++++ b/game/openvibe.games/scripts/weapon_manifest.txt
@@@ -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
ddiff --git a/game/openvibe.games/scripts/weapon_physcannon.txt b/game/openvibe.games/scripts/weapon_physcannon.txt
nnew file mode 100644
iindex 0000000..e455b97
---- /dev/null
++++ b/game/openvibe.games/scripts/weapon_physcannon.txt
@@@ -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
ddiff --git a/game/openvibe.games/scripts/weapon_pistol.txt b/game/openvibe.games/scripts/weapon_pistol.txt
nnew file mode 100644
iindex 0000000..da1aeb0
---- /dev/null
++++ b/game/openvibe.games/scripts/weapon_pistol.txt
@@@ -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
ddiff --git a/game/openvibe.games/scripts/weapon_rpg.txt b/game/openvibe.games/scripts/weapon_rpg.txt
nnew file mode 100644
iindex 0000000..580b0b8
---- /dev/null
++++ b/game/openvibe.games/scripts/weapon_rpg.txt
@@@ -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
ddiff --git a/game/openvibe.games/scripts/weapon_shotgun.txt b/game/openvibe.games/scripts/weapon_shotgun.txt
nnew file mode 100644
iindex 0000000..c00c49b
---- /dev/null
++++ b/game/openvibe.games/scripts/weapon_shotgun.txt
@@@ -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
ddiff --git a/game/openvibe.games/scripts/weapon_slam.txt b/game/openvibe.games/scripts/weapon_slam.txt
nnew file mode 100644
iindex 0000000..d1f7423
---- /dev/null
++++ b/game/openvibe.games/scripts/weapon_slam.txt
@@@ -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"
++ }
++}
ddiff --git a/game/openvibe.games/scripts/weapon_smg1.txt b/game/openvibe.games/scripts/weapon_smg1.txt
nnew file mode 100644
iindex 0000000..c3a67a8
---- /dev/null
++++ b/game/openvibe.games/scripts/weapon_smg1.txt
@@@ -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
ddiff --git a/game/openvibe.games/scripts/weapon_stunstick.txt b/game/openvibe.games/scripts/weapon_stunstick.txt
nnew file mode 100644
iindex 0000000..73c9948
---- /dev/null
++++ b/game/openvibe.games/scripts/weapon_stunstick.txt
@@@ -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
ddiff --git a/hammer/vmf/dr_openvibe_dev.vmf b/hammer/vmf/dr_openvibe_dev.vmf
iindex 2fb0118..3b24ba9 100644
---- a/hammer/vmf/dr_openvibe_dev.vmf
++++ b/hammer/vmf/dr_openvibe_dev.vmf
@@@ -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
{
ddiff --git a/hammer/vmf/fw_openvibe_dev.vmf b/hammer/vmf/fw_openvibe_dev.vmf
iindex be27285..fa53b87 100644
---- a/hammer/vmf/fw_openvibe_dev.vmf
++++ b/hammer/vmf/fw_openvibe_dev.vmf
@@@ -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
{
ddiff --git a/hammer/vmf/ov_hub.vmf b/hammer/vmf/ov_hub.vmf
iindex 38aa556..1acdf67 100644
---- a/hammer/vmf/ov_hub.vmf
++++ b/hammer/vmf/ov_hub.vmf
@@@ -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
{
ddiff --git a/hammer/vmf/ph_openvibe_dev.vmf b/hammer/vmf/ph_openvibe_dev.vmf
iindex b5480db..83716c9 100644
---- a/hammer/vmf/ph_openvibe_dev.vmf
++++ b/hammer/vmf/ph_openvibe_dev.vmf
@@@ -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
{
ddiff --git a/hammer/vmf/tt_openvibe_dev.vmf b/hammer/vmf/tt_openvibe_dev.vmf
iindex 852d80a..6210f4b 100644
---- a/hammer/vmf/tt_openvibe_dev.vmf
++++ b/hammer/vmf/tt_openvibe_dev.vmf
@@@ -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
{
ddiff --git a/launcher/main.js b/launcher/main.js
iindex 7cccbee..62bd1d8 100644
---- a/launcher/main.js
++++ b/launcher/main.js
@@@ -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/hosts
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;
return launchGame(srv.ip, srv.port);
ddiff --git a/servers/local-servers.json b/servers/local-servers.json
iindex c59d800..8a08ac5 100644
---- a/servers/local-servers.json
++++ b/servers/local-servers.json
@@@ -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
}
ddiff --git a/tools/backup-postgres.sh b/tools/backup-postgres.sh
nnew file mode 100755
iindex 0000000..ae14cd2
---- /dev/null
++++ b/tools/backup-postgres.sh
@@@ -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"
ddiff --git a/tools/dev-down.sh b/tools/dev-down.sh
iindex 6877148..0ebe765 100755
---- a/tools/dev-down.sh
++++ b/tools/dev-down.sh
@@@ -25,7 +25,7 @@ done
kill_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; do
if [[ "$pid" != "$$" ]]; then
kill "-$signal" "$pid" 2>/dev/null || true
fi
ddiff --git a/tools/generate-dev-vmfs.mjs b/tools/generate-dev-vmfs.mjs
iindex dadadfd..a3e409d 100644
---- a/tools/generate-dev-vmfs.mjs
++++ b/tools/generate-dev-vmfs.mjs
@@@ -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),
),
];
ddiff --git a/tools/run-client-proton.sh b/tools/run-client-proton.sh
iindex af655ce..80bce74 100755
---- a/tools/run-client-proton.sh
++++ b/tools/run-client-proton.sh
@@@ -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
ddiff --git a/tools/run-client.sh b/tools/run-client.sh
iindex 380d08a..33a2d29 100755
---- a/tools/run-client.sh
++++ b/tools/run-client.sh
@@@ -26,4 +26,6 @@ exec "$LAUNCHER" \
-dev \
-sw \
-w 1280 \
-- -h 720
++ -h 720 \
++ -port 27115 \
++ -clientport 27105
ddiff --git a/tools/run-server.sh b/tools/run-server.sh
iindex de76475..9f7c4dd 100755
---- a/tools/run-server.sh
++++ b/tools/run-server.sh
@@@ -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" ]]; then
echo "Missing 64-bit SRCDS at $SRCDS/srcds_linux64" >&2
echo "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" ]]; then
printf '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_output
else
-- map_input | "${server_cmd[@]}"
++ map_input | "${server_cmd[@]}" | filter_output
fi
else
exec "${server_cmd[@]}" +map "$MAP"
ddiff --git a/tools/run-sidecar.sh b/tools/run-sidecar.sh
iindex 00c36cb..d81d29d 100755
---- a/tools/run-sidecar.sh
++++ b/tools/run-sidecar.sh
@@@ -25,12 +25,21 @@ if [[ -d "$ROOT/game/openvibe.games/logs" ]]; then
LOG_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"
ddiff --git a/tools/smoke-api.mjs b/tools/smoke-api.mjs
iindex a9989b8..f0cb5ed 100644
---- a/tools/smoke-api.mjs
++++ b/tools/smoke-api.mjs
@@@ -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,
});
ddiff --git a/tools/smoke-srcds.sh b/tools/smoke-srcds.sh
iindex 0d030f7..5fda7b1 100755
---- a/tools/smoke-srcds.sh
++++ b/tools/smoke-srcds.sh
@@@ -11,6 +11,8 @@ run_check() {
local map="$3"
local log="/tmp/openvibe-srcds-$name.log"
++ rm -f "$log"
++
set +e
timeout "${OPENVIBE_SRCDS_SMOKE_TIMEOUT:-70}s" "$ROOT/tools/$script" >"$log" 2>&1
local code=$?
@@@ -34,7 +36,13 @@ run_check() {
exit 1
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"
tail -120 "$log"
exit 1