Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions src/components/Game.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import React, { useRef, useState, useCallback, useEffect, useMemo } from 'react';
import { useGame } from '@/context/GameContext';
import { Tool } from '@/types/game';
import { Tool, TOOL_INFO } from '@/types/game';
import { useMobile } from '@/hooks/useMobile';
import { MobileToolbar } from '@/components/mobile/MobileToolbar';
import { MobileTopBar } from '@/components/mobile/MobileTopBar';
Expand Down Expand Up @@ -39,6 +39,15 @@ import { CanvasIsometricGrid } from '@/components/game/CanvasIsometricGrid';
// Cargo type names for notifications
const CARGO_TYPE_NAMES = [msg('containers'), msg('bulk materials'), msg('oil')];

// Map shortcut key -> tool (from TOOL_INFO)
const KEY_TO_TOOL: Record<string, Tool> = (() => {
const map: Record<string, Tool> = {};
for (const [tool, info] of Object.entries(TOOL_INFO)) {
if (info.shortcut) map[info.shortcut] = tool as Tool;
}
return map;
})();

export default function Game({ onExit }: { onExit?: () => void }) {
const gt = useGT();
const m = useMessages();
Expand Down Expand Up @@ -171,14 +180,18 @@ export default function Game({ onExit }: { onExit?: () => void }) {
} else if (state.selectedTool !== 'select') {
setTool('select');
}
} else if (e.key === 'b' || e.key === 'B') {
e.preventDefault();
setTool('bulldoze');
} else if (e.key === 'p' || e.key === 'P') {
e.preventDefault();
// Toggle pause/unpause: if paused (speed 0), resume to normal (speed 1)
// If running, pause (speed 0)
setSpeed(state.speed === 0 ? 1 : 0);
} else if (!e.metaKey && !e.ctrlKey && !e.altKey) {
const key = e.key.length === 1 ? e.key.toLowerCase() : e.key;
const tool = KEY_TO_TOOL[key];
if (tool) {
e.preventDefault();
setTool(tool);
}
}
};

Expand Down Expand Up @@ -216,8 +229,8 @@ export default function Game({ onExit }: { onExit?: () => void }) {
clearTriggeredCheat();
break;
}
}, [triggeredCheat, addMoney, addNotification, clearTriggeredCheat]);
}, [triggeredCheat, addMoney, addNotification, clearTriggeredCheat, gt]);

// Track barge deliveries to show occasional notifications
const bargeDeliveryCountRef = useRef(0);

Expand Down
4 changes: 2 additions & 2 deletions src/components/game/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ const HoverSubmenu = React.memo(function HoverSubmenu({
}`}
title={`${m(info.description)} - Cost: $${info.cost.toLocaleString()}`}
>
<span className="flex-1 text-left truncate">{m(info.name)}</span>
<span className="flex-1 text-left truncate">{info.shortcut ? `(${info.shortcut}) ` : ''}{m(info.name)}</span>
<span className={`text-xs ${isSelected ? 'opacity-80' : 'opacity-50'}`}>${info.cost.toLocaleString()}</span>
</Button>
);
Expand Down Expand Up @@ -648,7 +648,7 @@ export const Sidebar = React.memo(function Sidebar({ onExit }: { onExit?: () =>
}`}
title={`${m(info.description)}${info.cost > 0 ? ` - Cost: $${info.cost}` : ''}`}
>
<span className="flex-1 text-left truncate">{m(info.name)}</span>
<span className="flex-1 text-left truncate">{info.shortcut ? `(${info.shortcut}) ` : ''}{m(info.name)}</span>
{info.cost > 0 && (
<span className="text-xs opacity-60">${info.cost}</span>
)}
Expand Down
16 changes: 15 additions & 1 deletion src/components/ui/CommandMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ interface MenuItem {
name: unknown; // Raw message object from msg()
description: unknown; // Raw message object from msg()
cost?: number;
shortcut?: string; // Keyboard shortcut from TOOL_INFO
category: string;
keywords: string[];
}
Expand Down Expand Up @@ -61,6 +62,7 @@ function buildMenuItems(): MenuItem[] {
name: info.name,
description: info.description,
cost: info.cost,
shortcut: info.shortcut,
category: 'tools',
keywords: [info.name.toLowerCase(), tool, 'tool', 'infrastructure'],
});
Expand All @@ -77,6 +79,7 @@ function buildMenuItems(): MenuItem[] {
name: info.name,
description: info.description,
cost: info.cost,
shortcut: info.shortcut,
category: 'zones',
keywords: [info.name.toLowerCase(), tool, 'zone'],
});
Expand All @@ -101,6 +104,7 @@ function buildMenuItems(): MenuItem[] {
name: info.name,
description: info.description,
cost: info.cost,
shortcut: info.shortcut,
category: 'zoning',
keywords,
});
Expand All @@ -117,6 +121,7 @@ function buildMenuItems(): MenuItem[] {
name: info.name,
description: info.description,
cost: info.cost,
shortcut: info.shortcut,
category: 'services',
keywords: [info.name.toLowerCase(), tool, 'service', 'building'],
});
Expand All @@ -133,6 +138,7 @@ function buildMenuItems(): MenuItem[] {
name: info.name,
description: info.description,
cost: info.cost,
shortcut: info.shortcut,
category: 'parks',
keywords: [info.name.toLowerCase(), tool, 'park', 'green', 'nature', 'recreation', 'entertainment'],
});
Expand All @@ -149,6 +155,7 @@ function buildMenuItems(): MenuItem[] {
name: info.name,
description: info.description,
cost: info.cost,
shortcut: info.shortcut,
category: 'sports',
keywords: [info.name.toLowerCase(), tool, 'sports', 'recreation', 'field'],
});
Expand All @@ -165,6 +172,7 @@ function buildMenuItems(): MenuItem[] {
name: info.name,
description: info.description,
cost: info.cost,
shortcut: info.shortcut,
category: 'waterfront',
keywords: [info.name.toLowerCase(), tool, 'water', 'waterfront', 'dock', 'pier', 'marina'],
});
Expand All @@ -181,6 +189,7 @@ function buildMenuItems(): MenuItem[] {
name: info.name,
description: info.description,
cost: info.cost,
shortcut: info.shortcut,
category: 'community',
keywords: [info.name.toLowerCase(), tool, 'community', 'building'],
});
Expand All @@ -197,6 +206,7 @@ function buildMenuItems(): MenuItem[] {
name: info.name,
description: info.description,
cost: info.cost,
shortcut: info.shortcut,
category: 'utilities',
keywords: [info.name.toLowerCase(), tool, 'utility', 'power', 'water', 'infrastructure', 'transit', 'station', 'train'],
});
Expand All @@ -213,6 +223,7 @@ function buildMenuItems(): MenuItem[] {
name: info.name,
description: info.description,
cost: info.cost,
shortcut: info.shortcut,
category: 'special',
keywords: [info.name.toLowerCase(), tool, 'special', 'landmark', 'attraction'],
});
Expand Down Expand Up @@ -456,7 +467,10 @@ export function CommandMenu() {
)}
>
<div className="flex flex-col gap-0.5 min-w-0">
<span className="font-medium truncate">{m(item.name as Parameters<typeof m>[0])}</span>
<span className="font-medium truncate">
{item.shortcut ? `(${item.shortcut}) ` : ''}
{m(item.name as Parameters<typeof m>[0])}
</span>
<span className={cn(
'text-xs truncate',
isSelected ? 'text-primary-foreground/70' : 'text-muted-foreground'
Expand Down
23 changes: 12 additions & 11 deletions src/games/isocity/types/game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,24 @@ export interface ToolInfo {
cost: number;
description: string;
size?: number;
shortcut?: string;
}

export const TOOL_INFO: Record<Tool, ToolInfo> = {
select: { name: msg('Select'), cost: 0, description: msg('Click to view tile info') },
bulldoze: { name: msg('Bulldoze'), cost: 10, description: msg('Remove buildings and zones') },
road: { name: msg('Road'), cost: 25, description: msg('Connect your city') },
rail: { name: msg('Rail'), cost: 40, description: msg('Build railway tracks') },
subway: { name: msg('Subway'), cost: 50, description: msg('Underground transit') },
select: { name: msg('Select'), cost: 0, description: msg('Click to view tile info'), shortcut: 'v' },
bulldoze: { name: msg('Bulldoze'), cost: 10, description: msg('Remove buildings and zones'), shortcut: 'b' },
road: { name: msg('Road'), cost: 25, description: msg('Connect your city'), shortcut: 'r' },
rail: { name: msg('Rail'), cost: 40, description: msg('Build railway tracks'), shortcut: 'l' },
subway: { name: msg('Subway'), cost: 50, description: msg('Underground transit'), shortcut: 'u' },
expand_city: { name: msg('Expand City'), cost: 0, description: msg('Add 15 tiles to each edge') },
shrink_city: { name: msg('Shrink City'), cost: 0, description: msg('Remove 15 tiles from each edge') },
tree: { name: msg('Tree'), cost: 15, description: msg('Plant trees to improve environment') },
zone_residential: { name: msg('Residential'), cost: 50, description: msg('Zone for housing') },
zone_commercial: { name: msg('Commercial'), cost: 50, description: msg('Zone for shops and offices') },
zone_industrial: { name: msg('Industrial'), cost: 50, description: msg('Zone for factories') },
zone_dezone: { name: msg('De-zone'), cost: 0, description: msg('Remove zoning') },
zone_water: { name: msg('Water Terraform'), cost: 50000, description: msg('Terraform land into water') },
zone_land: { name: msg('Land Terraform'), cost: 50000, description: msg('Terraform water into land') },
zone_residential: { name: msg('Residential'), cost: 50, description: msg('Zone for housing'), shortcut: '1' },
zone_commercial: { name: msg('Commercial'), cost: 50, description: msg('Zone for shops and offices'), shortcut: '2' },
zone_industrial: { name: msg('Industrial'), cost: 50, description: msg('Zone for factories'), shortcut: '3' },
zone_dezone: { name: msg('De-zone'), cost: 0, description: msg('Remove zoning'), shortcut: 'x' },
zone_water: { name: msg('Water Terraform'), cost: 50000, description: msg('Terraform land into water'), shortcut: 'o' },
zone_land: { name: msg('Land Terraform'), cost: 50000, description: msg('Terraform water into land'), shortcut: 'g' },
police_station: { name: msg('Police'), cost: 500, description: msg('Increase safety'), size: 1 },
fire_station: { name: msg('Fire Station'), cost: 500, description: msg('Fight fires'), size: 1 },
hospital: { name: msg('Hospital'), cost: 1000, description: msg('Improve health (2x2)'), size: 2 },
Expand Down