docs: Add MTG comprehensive rules and pack generation algorithms, remove old rulebook and development documentation, and update GameView.
Some checks failed
Build and Deploy / build (push) Failing after 10s
Some checks failed
Build and Deploy / build (push) Failing after 10s
This commit is contained in:
@@ -1,8 +0,0 @@
|
|||||||
# Development Status (Central)
|
|
||||||
|
|
||||||
## Active Tasks
|
|
||||||
- [x] Enable Clear Session Button (2025-12-20)
|
|
||||||
|
|
||||||
## Devlog Index
|
|
||||||
- [Enable Clear Session](./devlog/2025-12-20-014500_enable_clear_session.md) - Improved UI/UX for session clearing in CubeManager.
|
|
||||||
- [Bot Actions](./devlog/2025-12-22-114000_bot_actions.md) - Implemented simple bot AI for playing lands, casting creatures, and passing priority.
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
# Enable Clear Session Button in Pack Generator
|
|
||||||
|
|
||||||
## Object
|
|
||||||
Enable and improve the "Clear Session" button in the Cube Manager (Pack Generator) to allow users to restart the generation process from a clean state.
|
|
||||||
|
|
||||||
## Changes
|
|
||||||
- Modified `CubeManager.tsx`:
|
|
||||||
- Updated `handleReset` logic (verified).
|
|
||||||
- enhanced "Clear Session" button styling to be more visible (red border/text) and indicate its destructive nature.
|
|
||||||
- Added `disabled={loading}` to prevent state conflicts during active operations.
|
|
||||||
- **Replaced `window.confirm` with a double-click UI confirmation pattern** to ensure reliability and better UX (fixed issue where native confirmation dialog was failing).
|
|
||||||
|
|
||||||
## Status
|
|
||||||
- [x] Implementation complete.
|
|
||||||
- [x] Verified logic for `localStorage` clearing.
|
|
||||||
- [x] Verified interaction in browser (button changes state, clears data on second click).
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
# Bot Logic Implementation
|
|
||||||
|
|
||||||
## Changes
|
|
||||||
- **Client/Server Types**: Added `isBot` to `PlayerState` interface.
|
|
||||||
- **GameManager**:
|
|
||||||
- Updated `createGame` to persist `isBot` flag from room players.
|
|
||||||
- Implemented `processBotActions` method:
|
|
||||||
- **Mulligan**: Always keeps hand.
|
|
||||||
- **Main Phase**: Plays a Land if available and not played yet.
|
|
||||||
- **Main Phase**: Casts first available Creature card from hand (simplified cost check).
|
|
||||||
- **Combat**: Attacks with all available creatures.
|
|
||||||
- **Default**: Passes priority.
|
|
||||||
- Added `triggerBotCheck` public method to manually trigger bot automation (e.g. at game start).
|
|
||||||
- Updated `handleStrictAction` to include a `while` loop that processes consecutive bot turns until a human receives priority.
|
|
||||||
- **Server Entry (index.ts)**:
|
|
||||||
- Injected `gameManager.triggerBotCheck(roomId)` at all game start points (Normal start, Solo test, Deck timeout, etc.) to ensure bots act immediately if they win the coin flip or during mulligan.
|
|
||||||
|
|
||||||
## Bot Behavior
|
|
||||||
The bots are currently "Aggressive/Linear":
|
|
||||||
1. They essentially dump their hand (Lands -> Creatures).
|
|
||||||
2. They always attack with everything.
|
|
||||||
3. They never block.
|
|
||||||
4. They pass priority instantly if they can't do anything.
|
|
||||||
|
|
||||||
## Future Improvements
|
|
||||||
- Implement mana cost checking (currently relying on loose engine rules or implicit valid state).
|
|
||||||
- Implement target selection logic (currently casting only if no targets needed or using empty array).
|
|
||||||
- Implement blocking logic.
|
|
||||||
- Implement "Smart" mulligans (currently always keep).
|
|
||||||
@@ -675,7 +675,7 @@ export const GameView: React.FC<GameViewProps> = ({ gameState, currentPlayerId }
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Opponent Battlefield */}
|
{/* Opponent Battlefield */}
|
||||||
<div className="flex-1 w-full relative perspective-1000">
|
<div className="flex-1 w-full relative perspective-1000 z-0">
|
||||||
<div
|
<div
|
||||||
className="w-full h-full relative"
|
className="w-full h-full relative"
|
||||||
style={{
|
style={{
|
||||||
@@ -683,43 +683,73 @@ export const GameView: React.FC<GameViewProps> = ({ gameState, currentPlayerId }
|
|||||||
transformOrigin: 'center bottom',
|
transformOrigin: 'center bottom',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{oppBattlefield.map(card => {
|
{(() => {
|
||||||
const isAttacking = card.attacking === currentPlayerId; // They are attacking ME
|
// Organize Opponent Cards
|
||||||
const isBlockedByMe = Array.from(proposedBlockers.values()).includes(card.instanceId);
|
const oppLands = oppBattlefield.filter(c => c.types?.includes('Land') && !c.types?.includes('Creature'));
|
||||||
|
const oppCreatures = oppBattlefield.filter(c => !c.types?.includes('Land') || c.types?.includes('Creature'));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className="w-full h-full flex flex-col justify-end pb-4">
|
||||||
key={card.instanceId}
|
{/* Back Row: Lands (Top) */}
|
||||||
className="absolute transition-all duration-300 ease-out"
|
<div className="flex justify-center items-end -space-x-8 mb-4 opacity-90 scale-90 origin-bottom">
|
||||||
style={{
|
{oppLands.map((card, idx) => (
|
||||||
left: `${card.position?.x || 50}%`,
|
<div key={card.instanceId} style={{ zIndex: idx }} className="pointer-events-auto">
|
||||||
top: `${card.position?.y || 50}%`,
|
<CardComponent
|
||||||
zIndex: Math.floor((card.position?.y || 0)),
|
card={card}
|
||||||
transform: isAttacking ? 'translateY(40px) scale(1.1)' : 'none' // Move towards me
|
viewMode="cutout"
|
||||||
}}
|
className="w-20 h-20 rounded shadow-sm"
|
||||||
>
|
onDragStart={() => { }}
|
||||||
<CardComponent
|
onClick={() => { }}
|
||||||
card={card}
|
onMouseEnter={() => setHoveredCard(card)}
|
||||||
viewMode="cutout"
|
onMouseLeave={() => setHoveredCard(null)}
|
||||||
onDragStart={() => { }}
|
/>
|
||||||
onClick={() => { }}
|
</div>
|
||||||
onMouseEnter={() => setHoveredCard(card)}
|
))}
|
||||||
onMouseLeave={() => setHoveredCard(null)}
|
{oppLands.length === 0 && <div className="h-20" />}
|
||||||
className={`
|
</div>
|
||||||
w-24 h-24 rounded shadow-sm
|
|
||||||
${isAttacking ? "ring-4 ring-red-600 shadow-[0_0_20px_rgba(220,38,38,0.6)]" : ""}
|
{/* Front Row: Creatures (Bottom) */}
|
||||||
${isBlockedByMe ? "ring-4 ring-blue-500" : ""}
|
<div className="flex justify-center items-end gap-2 flex-wrap px-8">
|
||||||
`}
|
{oppCreatures.map(card => {
|
||||||
/>
|
const isAttacking = card.attacking === currentPlayerId; // They are attacking ME
|
||||||
<DroppableZone id={card.instanceId} data={{ type: 'card' }} className="absolute inset-0 rounded-lg" />
|
const isBlockedByMe = Array.from(proposedBlockers.values()).includes(card.instanceId);
|
||||||
{isAttacking && (
|
|
||||||
<div className="absolute -top-4 left-1/2 -translate-x-1/2 bg-red-600 text-white text-[10px] font-bold px-2 py-0.5 rounded shadow">
|
return (
|
||||||
ATTACKING
|
<div
|
||||||
</div>
|
key={card.instanceId}
|
||||||
)}
|
className="relative transition-all duration-300 ease-out pointer-events-auto"
|
||||||
|
style={{
|
||||||
|
transform: isAttacking ? 'translateY(40px) scale(1.1)' : 'none', // Attack moves "Forward" (Down)
|
||||||
|
zIndex: 10
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CardComponent
|
||||||
|
card={card}
|
||||||
|
viewMode="cutout"
|
||||||
|
onMouseEnter={() => setHoveredCard(card)}
|
||||||
|
onMouseLeave={() => setHoveredCard(null)}
|
||||||
|
onDragStart={() => { }}
|
||||||
|
onClick={() => { }}
|
||||||
|
className={`
|
||||||
|
w-24 h-24 rounded shadow-sm
|
||||||
|
${isAttacking ? "ring-4 ring-red-600 shadow-[0_0_20px_rgba(220,38,38,0.6)]" : ""}
|
||||||
|
${isBlockedByMe ? "ring-4 ring-blue-500" : ""}
|
||||||
|
`}
|
||||||
|
/>
|
||||||
|
<DroppableZone id={card.instanceId} data={{ type: 'card' }} className="absolute inset-0 rounded-lg" />
|
||||||
|
|
||||||
|
{isAttacking && (
|
||||||
|
<div className="absolute -top-4 left-1/2 -translate-x-1/2 bg-red-600 text-white text-[10px] font-bold px-2 py-0.5 rounded shadow z-20">
|
||||||
|
ATTACKING
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user