吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 942|回复: 21
上一主题 下一主题
收起左侧

[其他原创] 《人生重开模拟器・打工人本地重生版》-第一版

[复制链接]
跳转到指定楼层
楼主
Summer000 发表于 2026-7-3 08:41 回帖奖励
上班太无聊了,想了想自己走过的路,真的是有挫折有激励有进步,随手借助AI写了一个人生重开模拟器・打工人本地重生版的游戏,试了试,还行。
[backcolor=lab(9.03835 1.15298 1.92955)]
主要实现了以下核心功能:
[backcolor=lab(9.03835 1.15298 1.92955)]
核心玩法
[CSS] 纯文本查看 复制代码
import React from 'react';[/size][/font][/backcolor][/color][/size][/font][/backcolor][/color]
// 状态表情组件 - 根据数值显示不同表情
export function StatusEmoji({ value, type }: { value: number; type: 'brain' | 'physique' | 'family' | 'dignity' | 'mood' | 'savings' }) {
  const getEmoji = () => {
    if (type === 'savings') {
      if (value >= 500000) return '💰';
      if (value >= 100000) return '💵';
      if (value >= 0) return '💸';
      if (value >= -50000) return '😰';
      return '😭';
    }
    
    // 其他属性(0-100)
    if (value >= 80) return '😄';
    if (value >= 60) return '😊';
    if (value >= 40) return '😐';
    if (value >= 20) return '😔';
    return '😢';
  };
  
  return (
    <span className="animate-float text-2xl ml-2 inline-block">
      {getEmoji()}
    </span>
  );
}

// 状态趋势图标
export function TrendIndicator({ change }: { change: number }) {
  if (change === 0) return null;
  
  const isPositive = change > 0;
  
  return (
    <span className={`inline-flex items-center ml-2 ${
      isPositive ? 'text-green-400' : 'text-red-400'
    } animate-number-change`}>
      {isPositive ? '↑' : '↓'}
      <span className="text-xs ml-1">{Math.abs(change)}</span>
    </span>
  );
}

// 动态进度条组件
export function AnimatedProgressBar({ 
  value, 
  max = 100, 
  color = 'from-yellow-400 to-orange-500',
  showAnimation = true 
}: { 
  value: number; 
  max?: number; 
  color?: string;
  showAnimation?: boolean;
}) {
  const percentage = Math.min((value / max) * 100, 100);
  
  return (
    <div className="relative bg-slate-800 rounded-full h-3 overflow-hidden">
      <div 
        className={`bg-gradient-to-r ${color} h-3 rounded-full transition-all duration-500 ${
          showAnimation ? 'progress-bar-animated' : ''
        }`}
        style={{ width: `${percentage}%` }}
      />
      {showAnimation && (
        <div className="absolute inset-0 flex items-center justify-center">
          <div className="animate-shimmer w-full h-full" />
        </div>
      )}
    </div>
  );
}

// 数值显示组件(带动画)
export function AnimatedNumber({ 
  value, 
  prefix = '', 
  suffix = '',
  className = ''
}: { 
  value: number; 
  prefix?: string;
  suffix?: string;
  className?: string;
}) {
  const isPositive = value >= 0;
  
  return (
    <span className={`number-animate font-bold ${className} ${
      isPositive ? 'text-green-400' : 'text-red-400'
    }`}>
      {prefix}{value.toLocaleString()}{suffix}
    </span>
  );
}

// 年龄阶段图标
export function AgeStageIcon({ age }: { age: number }) {
  const getStageIcon = () => {
    if (age < 22) return '&#127891;';
    if (age < 30) return '&#128188;';
    if (age < 40) return '&#128104;&#8205;&#128188;';
    if (age < 50) return '&#129491;';
    if (age < 60) return '&#128116;';
    return '&#129702;';
  };
  
  const getStageName = () => {
    if (age < 22) return '校园阶段';
    if (age < 30) return '职场新人';
    if (age < 40) return '职场成熟';
    if (age < 50) return '中年阶段';
    if (age < 60) return '退休前期';
    return '人生终点';
  };
  
  return (
    <div className="flex items-center animate-slide-in">
      <span className="text-3xl mr-2 animate-float">{getStageIcon()}</span>
      <span className="text-sm text-slate-400">{getStageName()}</span>
    </div>
  );
}

// 结局类型图标
export function EndingTypeIcon({ category }: { category: string }) {
  const icons: Record<string, string> = {
    highlight: '&#127942;',
    stable: '&#127968;',
    tragic: '&#128148;',
    chill: '&#127958;&#65039;',
    bottom: '&#9888;&#65039;',
  };
  
  return (
    <span className="text-4xl animate-bounce-in">
      {icons[category] || '&#10067;'}
    </span>
  );
}

// 天赋闪光效果
export function TalentGlow({ rarity }: { rarity: string }) {
  if (rarity !== 'orange') return null;
  
  return (
    <div className="absolute inset-0 animate-glow rounded-lg pointer-events-none" />
  );
}

// 加载动画
export function LoadingSpinner() {
  return (
    <div className="flex items-center justify-center">
      <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-yellow-400" />
    </div>
  );
}

// 状态指示灯
export function StatusIndicator({ status }: { status: 'good' | 'warning' | 'danger' }) {
  return (
    <div className={`status-indicator status-${status} inline-block w-3 h-3 rounded-full mr-2 ${
      status === 'good' ? 'bg-green-400' : 
      status === 'warning' ? 'bg-yellow-400' : 
      'bg-red-400'
    } animate-pulse-custom`} />
  );
}

// 属性卡片组件(带动画)
export function AnimatedAttributeCard({ 
  name, 
  value, 
  change, 
  icon 
}: { 
  name: string; 
  value: number; 
  change?: number;
  icon?: string;
}) {
  const getStatusColor = (val: number) => {
    if (val >= 60) return 'from-green-400 to-emerald-500';
    if (val >= 40) return 'from-yellow-400 to-orange-500';
    return 'from-red-400 to-pink-500';
  };
  
  return (
    <div className="bg-slate-800 rounded-lg p-3 card-animate hover-lift">
      <div className="flex items-center justify-between mb-2">
        <div className="flex items-center">
          {icon && <span className="text-xl mr-2 animate-float">{icon}</span>}
          <span className="font-medium">{name}</span>
        </div>
        <div className="flex items-center">
          <AnimatedNumber value={value} className="text-lg" />
          {change !== undefined && <TrendIndicator change={change} />}
        </div>
      </div>
      <AnimatedProgressBar 
        value={value} 
        color={getStatusColor(value)}
        showAnimation={value > 60}
      />
    </div>
  );
}

// 事件类型图标
export function EventTypeIcon({ pool }: { pool: string }) {
  const icons: Record<string, string> = {
    campus: '&#127891;',
    workplace: '&#128188;',
    crisis: '&#9888;&#65039;',
    retirement: '&#127958;&#65039;',
  };
  
  return (
    <span className="text-2xl animate-bounce-in">
      {icons[pool] || '&#128204;'}
    </span>
  );
}
[JavaScript] 纯文本查看 复制代码
import React, { useState, useEffect, useCallback } from 'react';
import { GameEngine } from '../data/engine';
import { allTalents } from '../data/talents';
import { allEndings, getEndingCategoryName, getEndingCategoryColor } from '../data/endings';
import { exportLifeHistory, copyToClipboard } from '../data/storage';
import type { Attributes, Talent, SaveData, GameState, YearEvent } from '../types/game';
import { ATTRIBUTE_NAMES, ATTRIBUTE_DESCRIPTIONS } from '../types/game';
import {
  StatusEmoji,
  AnimatedProgressBar,
  AnimatedNumber,
  AgeStageIcon,
  EndingTypeIcon,
  TalentGlow,
  EventTypeIcon,
  AnimatedAttributeCard,
  TrendIndicator,
} from './AnimatedElements';

// 稀有度颜色映射
const rarityColors: Record<string, string> = {
  white: 'bg-gray-100 text-gray-800 border-gray-300',
  blue: 'bg-blue-100 text-blue-800 border-blue-300',
  purple: 'bg-purple-100 text-purple-800 border-purple-300',
  orange: 'bg-orange-100 text-orange-800 border-orange-300',
};

const rarityNames: Record<string, string> = {
  white: '普通',
  blue: '优质',
  purple: '稀有',
  orange: '逆天',
};

// 属性图标映射
const attributeIcons: Record<string, string> = {
  brain: '&#129504;',
  physique: '&#128170;',
  family: '&#127968;',
  dignity: '&#128081;',
  savings: '&#128176;',
  mood: '&#10084;&#65039;',
};

export function App() {
  const [engine] = useState(() => new GameEngine());
  const [state, setState] = useState<GameState>(() => engine.getState());
  const [page, setPage] = useState<'start' | 'talent' | 'attribute' | 'game' | 'ending' | 'collection' | 'saves'>('start');
  const [talentPool, setTalentPool] = useState<Talent[]>([]);
  const [selectedTalents, setSelectedTalents] = useState<string[]>([]);
  const [attributePoints, setAttributePoints] = useState<Partial<Attributes>>({});
  const [remainingPoints, setRemainingPoints] = useState(20);
  const [yearEvent, setYearEvent] = useState<YearEvent | null>(null);
  const [showEventModal, setShowEventModal] = useState(false);
  const [currentEventIndex, setCurrentEventIndex] = useState(0);
  const [saveList, setSaveList] = useState<SaveData[]>([]);
  
  // 更新状态
  const updateState = useCallback(() => {
    setState(engine.getState());
  }, [engine]);
  
  // 开始新游戏
  const startNewGame = () => {
    engine.resetGame();
    updateState();
    setPage('start');
  };
  
  // 选择重生路线
  const selectPath = (path: 'college' | 'career') => {
    engine.selectRebirthPath(path);
    const pool = engine.getRandomTalentPool();
    setTalentPool(pool);
    setSelectedTalents([]);
    updateState();
    setPage('talent');
  };
  
  // 选择天赋
  const toggleTalent = (talentId: string) => {
    const talent = allTalents.find(t => t.id === talentId);
    if (!talent) return;
    
    // 检查互斥
    if (talent.exclusive) {
      for (const excludedId of talent.exclusive) {
        if (selectedTalents.includes(excludedId)) {
          return; // 已选择互斥天赋,不能选择
        }
      }
    }
    
    if (selectedTalents.includes(talentId)) {
      setSelectedTalents(selectedTalents.filter(id => id !== talentId));
    } else if (selectedTalents.length < 3) {
      setSelectedTalents([...selectedTalents, talentId]);
    }
  };
  
  // 确认天赋选择
  const confirmTalents = () => {
    if (selectedTalents.length === 0) return;
    
    engine.selectTalents(selectedTalents);
    setAttributePoints({});
    setRemainingPoints(20);
    updateState();
    setPage('attribute');
  };
  
  // 分配属性点
  const adjustAttribute = (attr: keyof Attributes, delta: number) => {
    if (attr === 'savings' || attr === 'mood') return; // 不能分配存款和心气
    
    const current = attributePoints[attr] || 0;
    const newPoints = remainingPoints - delta;
    
    if (newPoints < 0 || current + delta < 0 || current + delta > 20) return;
    
    setAttributePoints({ ...attributePoints, [attr]: current + delta });
    setRemainingPoints(newPoints);
  };
  
  // 确认属性分配
  const confirmAttributes = () => {
    engine.assignAttributes(attributePoints);
    updateState();
    setPage('game');
  };
  
  // 推进一年
  const advanceYear = () => {
    const event = engine.advanceYear();
    updateState();
    
    if (event) {
      setYearEvent(event);
      if (event.events.length > 0) {
        setCurrentEventIndex(0);
        setShowEventModal(true);
      }
    } else {
      // 游戏结束
      setPage('ending');
    }
  };
  
  // 关闭事件弹窗
  const closeEventModal = () => {
    setShowEventModal(false);
    setYearEvent(null);
    setCurrentEventIndex(0);
    
    // 检查是否触发结局
    if (state.phase === 'ending') {
      setPage('ending');
    }
  };
  
  // 下一个事件
  const nextEvent = () => {
    if (yearEvent && currentEventIndex < yearEvent.events.length - 1) {
      setCurrentEventIndex(currentEventIndex + 1);
    } else {
      closeEventModal();
    }
  };
  
  // 快速推进到结局
  const fastForward = () => {
    while (state.age < 65 && state.phase === 'playing') {
      engine.advanceYear();
      updateState();
    }
    setPage('ending');
  };
  
  // 查看图鉴
  const viewCollection = () => {
    setPage('collection');
  };
  
  // 查看存档
  const viewSaves = () => {
    setSaveList(engine.getSaves());
    setPage('saves');
  };
  
  // 导出人生历程
  const exportHistory = async (save: SaveData) => {
    const text = exportLifeHistory(save);
    const success = await copyToClipboard(text);
    if (success) {
      alert('人生历程已复制到剪贴板!');
    } else {
      alert('复制失败,请手动复制');
    }
  };
  
  // 获取结局详情
  const getEndingDetail = () => {
    if (!state.currentEnding) return null;
    return allEndings.find(e => e.id === state.currentEnding);
  };
  
  return (
    <div className="min-h-screen bg-gradient-to-b from-slate-900 to-slate-800 text-white">
      {/* 主页 */}
      {page === 'start' && (
        <div className="flex flex-col items-center justify-center min-h-screen p-8 animate-fade-in">
          <div className="max-w-md w-full text-center">
            <h1 className="text-4xl font-bold mb-4 text-transparent bg-clip-text bg-gradient-to-r from-yellow-400 to-orange-500 animate-shimmer title-animate">
              人生重开·打工人本地重生模拟器
            </h1>
            <p className="text-slate-400 mb-8 text-sm animate-slide-up delay-100">
              带着前世记忆重生,规避职场陷阱,拒绝内卷躺平,开启理想人生
            </p>
            
            <div className="space-y-4 mb-8">
              <button
                onClick={() => selectPath('college')}
                className="w-full py-4 bg-gradient-to-r from-blue-500 to-purple-600 rounded-lg font-bold hover:opacity-90 transition-all btn-animate hover-scale animate-slide-up delay-200"
              >
                <span className="inline-flex items-center">
                  &#127891; 大学重生(18岁开局)
                </span>
              </button>
              <p className="text-xs text-slate-500 animate-slide-up delay-300">
                记得未来风口、房价走势、踩坑副业、渣男烂同事
              </p>
              
              <button
                onClick={() => selectPath('career')}
                className="w-full py-4 bg-gradient-to-r from-green-500 to-teal-600 rounded-lg font-bold hover:opacity-90 transition-all btn-animate hover-scale animate-slide-up delay-400"
              >
                <span className="inline-flex items-center">
                  &#128188; 入职重生(22岁开局)
                </span>
              </button>
              <p className="text-xs text-slate-500 animate-slide-up delay-500">
                记得领导性格、裁员名单、项目坑点、晋升潜规则
              </p>
            </div>
            
            <div className="flex gap-4 justify-center animate-slide-up delay-500">
              <button
                onClick={viewCollection}
                className="px-6 py-2 bg-slate-700 rounded-lg hover:bg-slate-600 transition-colors hover-lift"
              >
                &#128218; 结局图鉴
              </button>
              <button
                onClick={viewSaves}
                className="px-6 py-2 bg-slate-700 rounded-lg hover:bg-slate-600 transition-colors hover-lift"
              >
                &#128190; 轮回存档
              </button>
            </div>
            
            <div className="mt-8 text-xs text-slate-500 animate-fade-in delay-500">
              已解锁结局: {engine.getCollection().endings.length}/{allEndings.length} | 
              游戏次数: {engine.getCollection().totalGames}
            </div>
          </div>
        </div>
      )}
      
      {/* 天赋选择 */}
      {page === 'talent' && (
        <div className="min-h-screen p-8">
          <div className="max-w-2xl mx-auto">
            <div className="text-center mb-8">
              <h2 className="text-3xl font-bold mb-2">选择天赋(最多3个)</h2>
              <p className="text-slate-400">
                已选择 {selectedTalents.length}/3 个天赋
              </p>
            </div>
            
            <div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-8">
              {talentPool.map(talent => {
                const isSelected = selectedTalents.includes(talent.id);
                const isExcluded = talent.exclusive?.some(id => selectedTalents.includes(id));
                
                return (
                  <div
                    key={talent.id}
                    onClick={() => !isExcluded && toggleTalent(talent.id)}
                    className={`p-4 rounded-lg border-2 cursor-pointer transition-all ${
                      isSelected 
                        ? 'border-yellow-400 bg-yellow-400/20' 
                        : isExcluded 
                          ? 'border-slate-600 bg-slate-700/50 opacity-50 cursor-not-allowed'
                          : 'border-slate-600 bg-slate-700 hover:border-slate-500'
                    }`}
                  >
                    <div className="flex items-center gap-2 mb-2">
                      <span className={`px-2 py-1 rounded text-xs font-bold ${
                        rarityColors[talent.rarity]
                      }`}>
                        {rarityNames[talent.rarity]}
                      </span>
                      <h3 className="font-bold">{talent.name}</h3>
                    </div>
                    <p className="text-sm text-slate-400">{talent.description}</p>
                    {talent.exclusive && talent.exclusive.length > 0 && (
                      <p className="text-xs text-red-400 mt-2">
                        与 {talent.exclusive.map(id => allTalents.find(t => t.id === id)?.name).join('、')} 互斥
                      </p>
                    )}
                  </div>
                );
              })}
            </div>
            
            <div className="flex justify-center gap-4">
              <button
                onClick={() => setPage('start')}
                className="px-6 py-3 bg-slate-700 rounded-lg hover:bg-slate-600 transition-colors"
              >
                返回
              </button>
              <button
                onClick={confirmTalents}
                disabled={selectedTalents.length === 0}
                className="px-6 py-3 bg-gradient-to-r from-yellow-500 to-orange-600 rounded-lg font-bold hover:opacity-90 transition-opacity disabled:opacity-50 disabled:cursor-not-allowed"
              >
                确认选择
              </button>
            </div>
          </div>
        </div>
      )}
      
      {/* 属性分配 */}
      {page === 'attribute' && (
        <div className="min-h-screen p-8">
          <div className="max-w-xl mx-auto">
            <div className="text-center mb-8">
              <h2 className="text-3xl font-bold mb-2">分配属性点</h2>
              <p className="text-slate-400">
                剩余可分配点数: <span className="text-yellow-400 font-bold">{remainingPoints}</span>
              </p>
            </div>
            
            <div className="space-y-4 mb-8">
              {(['brain', 'physique', 'family', 'dignity'] as const).map(attr => (
                <div key={attr} className="bg-slate-700 rounded-lg p-4">
                  <div className="flex items-center justify-between mb-2">
                    <div>
                      <h3 className="font-bold">{ATTRIBUTE_NAMES[attr]}</h3>
                      <p className="text-xs text-slate-400">{ATTRIBUTE_DESCRIPTIONS[attr]}</p>
                    </div>
                    <div className="flex items-center gap-2">
                      <span className="text-sm text-slate-400">初始: {state.attributes[attr]}</span>
                      <span className="text-sm text-yellow-400">+{attributePoints[attr] || 0}</span>
                    </div>
                  </div>
                  
                  <div className="flex items-center gap-4">
                    <button
                      onClick={() => adjustAttribute(attr, -1)}
                      className="px-4 py-2 bg-slate-600 rounded hover:bg-slate-500 transition-colors"
                    >
                      -1
                    </button>
                    <div className="flex-1 bg-slate-800 rounded-full h-2">
                      <div 
                        className="bg-gradient-to-r from-yellow-400 to-orange-500 h-2 rounded-full transition-all"
                        style={{ width: `${((state.attributes[attr] + (attributePoints[attr] || 0)) / 100) * 100}%` }}
                      />
                    </div>
                    <button
                      onClick={() => adjustAttribute(attr, 1)}
                      className="px-4 py-2 bg-slate-600 rounded hover:bg-slate-500 transition-colors"
                    >
                      +1
                    </button>
                  </div>
                </div>
              ))}
            </div>
            
            <div className="flex justify-center gap-4">
              <button
                onClick={() => setPage('talent')}
                className="px-6 py-3 bg-slate-700 rounded-lg hover:bg-slate-600 transition-colors"
              >
                返回
              </button>
              <button
                onClick={confirmAttributes}
                className="px-6 py-3 bg-gradient-to-r from-yellow-500 to-orange-600 rounded-lg font-bold hover:opacity-90 transition-opacity"
              >
                开始人生
              </button>
            </div>
          </div>
        </div>
      )}
      
      {/* 游戏进行 */}
      {page === 'game' && (
        <div className="min-h-screen p-8 animate-fade-in">
          <div className="max-w-xl mx-auto">
            {/* 属性面板 */}
            <div className="bg-slate-700 rounded-lg p-4 mb-6 card-animate hover-lift">
              <div className="flex items-center justify-between mb-4">
                <h3 className="font-bold text-xl">当前属性</h3>
                <div className="flex items-center">
                  <AgeStageIcon age={state.age} />
                  <span className="text-2xl font-bold text-yellow-400 ml-4 animate-pulse-custom">{state.age}岁</span>
                </div>
              </div>
              
              <div className="grid grid-cols-2 gap-2 text-sm">
                {(['brain', 'physique', 'family', 'dignity', 'mood'] as const).map((attr, i) => (
                  <div key={attr} className={`flex items-center justify-between bg-slate-800 rounded p-2 animate-slide-in delay-${i * 100}`}>
                    <div className="flex items-center">
                      <span className="text-lg mr-2 animate-float">{attributeIcons[attr]}</span>
                      <span>{ATTRIBUTE_NAMES[attr]}</span>
                    </div>
                    <div className="flex items-center">
                      <span className="font-bold">{state.attributes[attr]}</span>
                      <StatusEmoji value={state.attributes[attr]} type={attr} />
                    </div>
                  </div>
                ))}
                <div className="flex items-center justify-between bg-slate-800 rounded p-2 col-span-2 animate-slide-in delay-500">
                  <div className="flex items-center">
                    <span className="text-lg mr-2 animate-float">{attributeIcons.savings}</span>
                    <span>存款</span>
                  </div>
                  <div className="flex items-center">
                    <AnimatedNumber 
                      value={state.attributes.savings} 
                      prefix="&#165;" 
                      className="text-base"
                    />
                    <StatusEmoji value={state.attributes.savings} type="savings" />
                  </div>
                </div>
              </div>
              
              {/* 属性进度条 */}
              <div className="mt-4 grid grid-cols-3 gap-2">
                {(['brain', 'physique', 'mood'] as const).map(attr => (
                  <div key={attr} className="text-xs">
                    <div className="flex items-center justify-between mb-1">
                      <span>{ATTRIBUTE_NAMES[attr]}</span>
                      <span>{state.attributes[attr]}</span>
                    </div>
                    <AnimatedProgressBar 
                      value={state.attributes[attr]} 
                      showAnimation={state.attributes[attr] > 60}
                    />
                  </div>
                ))}
              </div>
              
              {/* 持有天赋 */}
              {state.selectedTalents.length > 0 && (
                <div className="mt-4 pt-4 border-t border-slate-600">
                  <p className="text-xs text-slate-400 mb-2">持有天赋:</p>
                  <div className="flex flex-wrap gap-2">
                    {state.selectedTalents.map((id, i) => {
                      const talent = allTalents.find(t => t.id === id);
                      return talent ? (
                        <span 
                          key={id} 
                          className={`px-2 py-1 rounded text-xs animate-slide-in delay-${i * 100} ${
                            rarityColors[talent.rarity]
                          } ${talent.rarity === 'orange' ? 'animate-glow' : ''}`}
                        >
                          {talent.name}
                        </span>
                      ) : null;
                    })}
                  </div>
                </div>
              )}
            </div>
            
            {/* 历史记录 */}
            <div className="bg-slate-700 rounded-lg p-4 mb-6 max-h-60 overflow-y-auto animate-slide-up">
              <h3 className="font-bold mb-4">&#128220; 人生历程</h3>
              <div className="space-y-2 text-sm">
                {state.history.slice(-10).map((h, i) => (
                  <div key={i} className="bg-slate-800 rounded p-2 animate-slide-in delay-${i * 50}">
                    <span className="text-yellow-400 font-bold">{h.age}岁</span>
                    <span className="text-slate-400 ml-2">{h.description}</span>
                    {h.majorEvent && (
                      <span className="text-orange-400 text-xs ml-2">&#128204; {h.majorEvent}</span>
                    )}
                  </div>
                ))}
              </div>
            </div>
            
            {/* 操作按钮 */}
            <div className="flex flex-col gap-4">
              <button
                onClick={advanceYear}
                className="py-4 bg-gradient-to-r from-yellow-500 to-orange-600 rounded-lg font-bold hover:opacity-90 transition-all btn-animate hover-scale animate-pulse-custom"
              >
                <span className="inline-flex items-center">
                  &#128640; 推进一年 → {state.age + 1}岁
                </span>
              </button>
              <button
                onClick={fastForward}
                className="py-3 bg-slate-700 rounded-lg hover:bg-slate-600 transition-colors hover-lift"
              >
                &#9193; 快速推进到结局
              </button>
            </div>
          </div>
          
          {/* 事件弹窗 */}
          {showEventModal && yearEvent && yearEvent.events.length > 0 && (
            <div className="fixed inset-0 bg-black/80 flex items-center justify-center p-4 z-50 modal-animate">
              <div className="bg-slate-800 rounded-lg p-6 max-w-md w-full modal-content-animate hover-glow">
                <div className="flex items-center gap-3 mb-4">
                  <EventTypeIcon pool={yearEvent.events[currentEventIndex].eventId.split('_')[0]} />
                  <h3 className="text-xl font-bold text-yellow-400 animate-fade-in">
                    {yearEvent.events[currentEventIndex].title}
                  </h3>
                </div>
                
                <p className="text-slate-400 mb-4 animate-slide-in">
                  &#10003; 选择: {yearEvent.events[currentEventIndex].choice}
                </p>
                
                <div className="mb-4 animate-slide-up delay-100">
                  <p className="text-xs text-slate-500 mb-2">&#128202; 属性变化:</p>
                  <div className="flex flex-wrap gap-2">
                    {Object.entries(yearEvent.events[currentEventIndex].effects).map(([key, value]) => (
                      <span key={key} className={`px-3 py-1 rounded text-sm animate-bounce-in ${
                        value > 0 ? 'bg-green-900 text-green-400' : 'bg-red-900 text-red-400'
                      }`}>
                        {attributeIcons[key] || '&#128202;'} {ATTRIBUTE_NAMES[key as keyof Attributes] || key} 
                        <span className="ml-1">{value > 0 ? '+' : ''}{value}</span>
                      </span>
                    ))}
                  </div>
                </div>
                
                <button
                  onClick={nextEvent}
                  className="w-full py-3 bg-gradient-to-r from-yellow-500 to-orange-600 rounded-lg font-bold hover:opacity-90 transition-opacity btn-animate"
                >
                  {currentEventIndex < yearEvent.events.length - 1 ? '&#128073; 下一个事件' : '&#10024; 继续人生'}
                </button>
              </div>
            </div>
          )}
        </div>
      )}
      
      {/* 结局页面 */}
      {page === 'ending' && (
        <div className="min-h-screen p-8 animate-fade-in">
          <div className="max-w-md mx-auto text-center">
            <h2 className="text-3xl font-bold mb-2 animate-bounce-in">人生结束</h2>
            <p className="text-slate-400 mb-4 animate-slide-up delay-100">
              享年 {state.age} 岁
            </p>
            
            {getEndingDetail() && (
              <div className="bg-slate-700 rounded-lg p-6 mb-6 card-animate hover-glow">
                <div className="mb-4 animate-float">
                  <EndingTypeIcon category={getEndingDetail()!.category} />
                </div>
                <div className={`inline-block px-3 py-1 rounded-full mb-4 animate-pulse-custom ${
                  getEndingCategoryColor(getEndingDetail()!.category)
                }`}>
                  {getEndingCategoryName(getEndingDetail()!.category)}
                </div>
                <h3 className="text-2xl font-bold mb-2 text-yellow-400 animate-slide-up">
                  {getEndingDetail()!.name}
                </h3>
                <p className="text-slate-400 animate-slide-up delay-200">
                  {getEndingDetail()!.description}
                </p>
              </div>
            )}
            
            {/* 最终属性 */}
            <div className="bg-slate-700 rounded-lg p-4 mb-6 animate-slide-up delay-300">
              <h4 className="font-bold mb-2">&#128202; 最终属性</h4>
              <div className="grid grid-cols-2 gap-2 text-sm">
                {(['brain', 'physique', 'family', 'dignity', 'mood'] as const).map((attr, i) => (
                  <div key={attr} className={`flex items-center justify-between bg-slate-800 rounded p-2 animate-slide-in delay-${i * 100}`}>
                    <div className="flex items-center">
                      <span className="text-lg mr-2">{attributeIcons[attr]}</span>
                      <span>{ATTRIBUTE_NAMES[attr]}</span>
                    </div>
                    <AnimatedNumber value={state.attributes[attr]} />
                  </div>
                ))}
                <div className="flex items-center justify-between bg-slate-800 rounded p-2 col-span-2 animate-slide-in delay-500">
                  <div className="flex items-center">
                    <span className="text-lg mr-2">{attributeIcons.savings}</span>
                    <span>存款</span>
                  </div>
                  <AnimatedNumber 
                    value={state.attributes.savings} 
                    prefix="&#165;" 
                  />
                </div>
              </div>
            </div>
            
            <div className="flex flex-col gap-4 animate-slide-up delay-400">
              <button
                onClick={startNewGame}
                className="py-4 bg-gradient-to-r from-yellow-500 to-orange-600 rounded-lg font-bold hover:opacity-90 transition-all btn-animate hover-scale animate-pulse-custom"
              >
                &#128260; 再来一轮
              </button>
              <div className="flex gap-4">
                <button
                  onClick={viewCollection}
                  className="flex-1 py-3 bg-slate-700 rounded-lg hover:bg-slate-600 transition-colors hover-lift"
                >
                  &#128218; 结局图鉴
                </button>
                <button
                  onClick={viewSaves}
                  className="flex-1 py-3 bg-slate-700 rounded-lg hover:bg-slate-600 transition-colors hover-lift"
                >
                  &#128190; 轮回存档
                </button>
              </div>
            </div>
          </div>
        </div>
      )}
      
      {/* 图鉴页面 */}
      {page === 'collection' && (
        <div className="min-h-screen p-8 animate-fade-in">
          <div className="max-w-2xl mx-auto">
            <div className="text-center mb-8">
              <h2 className="text-3xl font-bold mb-2 animate-bounce-in">&#128218; 结局图鉴</h2>
              <p className="text-slate-400 animate-slide-up">
                已解锁 {engine.getCollection().endings.length}/{allEndings.length} 个结局
              </p>
            </div>
            
            <div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-8">
              {allEndings.map((ending, i) => {
                const unlocked = engine.getCollection().endings.includes(ending.id);
                
                return (
                  <div
                    key={ending.id}
                    className={`p-4 rounded-lg border-2 card-animate hover-lift ${
                      unlocked 
                        ? 'border-yellow-400 bg-yellow-400/20 animate-glow' 
                        : 'border-slate-600 bg-slate-700/50'
                    }`}
                    style={{ animationDelay: `${i * 50}ms` }}
                  >
                    <div className="flex items-center gap-2 mb-2">
                      <EndingTypeIcon category={ending.category} />
                      <span className={`px-2 py-1 rounded text-xs font-bold ${
                        getEndingCategoryColor(ending.category)
                      }`}>
                        {getEndingCategoryName(ending.category)}
                      </span>
                      <h3 className="font-bold">
                        {unlocked ? ending.name : '&#10067; ???'}
                      </h3>
                    </div>
                    <p className="text-sm text-slate-400 animate-slide-up">
                      {unlocked ? ending.description : '&#128274; 尚未解锁此结局'}
                    </p>
                    {unlocked && (
                      <div className="mt-2 flex items-center text-xs text-slate-500 animate-fade-in">
                        <span className="animate-float mr-2">&#11088;</span>
                        稀有度: {ending.rarity}/5
                      </div>
                    )}
                  </div>
                );
              })}
            </div>
            
            <button
              onClick={() => setPage('start')}
              className="w-full py-3 bg-slate-700 rounded-lg hover:bg-slate-600 transition-colors hover-lift animate-slide-up"
            >
              &#127968; 返回主页
            </button>
          </div>
        </div>
      )}
      
      {/* 存档页面 */}
      {page === 'saves' && (
        <div className="min-h-screen p-8 animate-fade-in">
          <div className="max-w-2xl mx-auto">
            <div className="text-center mb-8">
              <h2 className="text-3xl font-bold mb-2 animate-bounce-in">&#128190; 轮回存档</h2>
              <p className="text-slate-400 animate-slide-up">
                共 {saveList.length} 轮人生记录
              </p>
            </div>
            
            {saveList.length === 0 ? (
              <div className="text-center text-slate-500 py-12 animate-pulse-custom">
                &#128237; 还没有人生记录,快去开启第一轮吧!
              </div>
            ) : (
              <div className="space-y-4 mb-8">
                {saveList.map((save, i) => (
                  <div
                    key={save.id}
                    className="bg-slate-700 rounded-lg p-4 card-animate hover-lift"
                    style={{ animationDelay: `${i * 100}ms` }}
                  >
                    <div className="flex items-center justify-between mb-2">
                      <div>
                        <h3 className="font-bold text-yellow-400 animate-slide-in">
                          {save.ending?.name || '未知结局'}
                        </h3>
                        <p className="text-xs text-slate-400 animate-slide-in delay-100">
                          &#128197; {new Date(save.timestamp).toLocaleString()} | 享年 {save.gameState.age}岁
                        </p>
                      </div>
                      <div className="flex items-center">
                        {save.ending && <EndingTypeIcon category={save.ending.category} />}
                        <span className={`ml-2 px-2 py-1 rounded text-xs ${
                          save.ending ? getEndingCategoryColor(save.ending.category) : 'bg-slate-600'
                        } animate-fade-in`}>
                          {save.ending ? getEndingCategoryName(save.ending.category) : '未知'}
                        </span>
                      </div>
                    </div>
                    
                    <div className="grid grid-cols-3 gap-2 text-xs mb-2 animate-slide-up">
                      <div className="bg-slate-800 rounded p-2 flex items-center">
                        <span className="mr-1">{attributeIcons.savings}</span>
                        <span>存款: &#165;{save.gameState.attributes.savings.toLocaleString()}</span>
                      </div>
                      <div className="bg-slate-800 rounded p-2 flex items-center">
                        <span className="mr-1">{attributeIcons.mood}</span>
                        <span>心气: {save.gameState.attributes.mood}</span>
                      </div>
                      <div className="bg-slate-800 rounded p-2 flex items-center">
                        <span className="mr-1">{attributeIcons.physique}</span>
                        <span>体魄: {save.gameState.attributes.physique}</span>
                      </div>
                    </div>
                    
                    <div className="flex gap-2 animate-slide-up delay-200">
                      <button
                        onClick={() => exportHistory(save)}
                        className="flex-1 py-2 bg-slate-600 rounded hover:bg-slate-500 transition-colors hover-lift"
                      >
                        &#128228; 导出历程
                      </button>
                    </div>
                  </div>
                ))}
              </div>
            )}
            
            <button
              onClick={() => setPage('start')}
              className="w-full py-3 bg-slate-700 rounded-lg hover:bg-slate-600 transition-colors hover-lift animate-slide-up"
            >
              &#127968; 返回主页
            </button>
          </div>
        </div>
      )}
    </div>
  );
}
  • 六维属性系统(脑力、体魄、家底、尊严、存款、心气)
  • 双重生路线选择(大学重生 18 岁 / 入职重生 22 岁)
  • 天赋系统(4 种稀有度,最多 3 个天赋,含互斥机制)
  • 属性点分配(20 点可分配,影响初始状态)
[backcolor=lab(9.03835 1.15298 1.92955)]
游戏机制
  • 年龄推进系统(18-65 岁职场人生)
  • 自动收支计算(薪资、房租 / 房贷、赡养费、生活费)
  • 年度属性变化(天赋效果、自然变化)
  • 随机事件触发(校园 / 职场 / 危机 / 退休四大事件池)
[backcolor=lab(9.03835 1.15298 1.92955)]
结局系统
  • 五大类结局:高光逆袭 (22 个)、普通安稳 (26 个)、中年悲剧 (20 个)、躺平佛系 (12 个)、极端底层 (6 个)
  • 根据属性、存款、年龄、天赋、事件综合判定
  • 结局图鉴收集系统
[backcolor=lab(9.03835 1.15298 1.92955)]
本地功能
  • 轮回存档管理
  • 结局图鉴解锁
  • 人生历程导出(文本复制)
  • 无联网依赖,完全本地运行

8a98fe7bfa1b427d8a748eda09652540.png (73.33 KB, 下载次数: 1)

8a98fe7bfa1b427d8a748eda09652540.png

d6915f8e9d984c47a96902d6e634007e.png (92.45 KB, 下载次数: 0)

d6915f8e9d984c47a96902d6e634007e.png

70fc2f3c4ae4473b83f12ec7830725f3.png (94.38 KB, 下载次数: 0)

70fc2f3c4ae4473b83f12ec7830725f3.png

7f2cd7f2c46047b2ad36b63a7f9af143.png (92.45 KB, 下载次数: 0)

7f2cd7f2c46047b2ad36b63a7f9af143.png

993d171189ad44cb946cc431a27bb1fd.png (91.51 KB, 下载次数: 0)

993d171189ad44cb946cc431a27bb1fd.png

15ff5ca144824e8cbf49ba73468903e3.png (137.83 KB, 下载次数: 0)

15ff5ca144824e8cbf49ba73468903e3.png

c4e2e8cc38bf4bd196961f2974633b0d.png (120.75 KB, 下载次数: 0)

c4e2e8cc38bf4bd196961f2974633b0d.png

99215b9b55b4436982f58b85a0bd6893.png (99.91 KB, 下载次数: 0)

99215b9b55b4436982f58b85a0bd6893.png

873887a92d3846dab4a5f15724357ae4.png (87.31 KB, 下载次数: 0)

873887a92d3846dab4a5f15724357ae4.png

02047c5cd1364786aa1b5c512a2e6ff1.png (108.41 KB, 下载次数: 0)

02047c5cd1364786aa1b5c512a2e6ff1.png

3ff35bc4d388446c9768a67efe635e01.png (49.83 KB, 下载次数: 0)

3ff35bc4d388446c9768a67efe635e01.png

143c3af13ff140bebb3015bc0b0fa851.png (141.36 KB, 下载次数: 0)

143c3af13ff140bebb3015bc0b0fa851.png

免费评分

参与人数 3吾爱币 +3 收起 理由
zxcvbnm12 + 1 复出了
lcp427 + 1 谢谢@Thanks!
xyz0042 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
 楼主| Summer000 发表于 2026-7-3 09:41 |楼主
lcp427 发表于 2026-7-3 09:38
代码不完整哦,不给完整的没法体验哦

比较多,我看看能不能开个体验网址
推荐
Love0912 发表于 2026-7-3 09:05
居然没有放体验地址,好歹让我们上手体验下给你提提意见嘛~
4#
overlord012 发表于 2026-7-3 08:52
5#
 楼主| Summer000 发表于 2026-7-3 08:54 |楼主
overlord012 发表于 2026-7-3 08:52
看起来像是中国式家长中的闵二狗。。

看来憨厚老实才是重要配色
6#
datangshizhewu 发表于 2026-7-3 09:12
有点意思 想试试
7#
 楼主| Summer000 发表于 2026-7-3 09:20 |楼主
Love0912 发表于 2026-7-3 09:05
居然没有放体验地址,好歹让我们上手体验下给你提提意见嘛~

体验地址能放上去吗?我看版规好像不允许啊
8#
xujili168 发表于 2026-7-3 09:25
模拟人生,有趣
9#
lcp427 发表于 2026-7-3 09:38
代码不完整哦,不给完整的没法体验哦
10#
py5655 发表于 2026-7-3 09:50
有没有下载地址?想体验重开的乐趣。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-7-3 13:36

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表