add THUNLP Demo
@ -12,13 +12,38 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowspan="5"><a href="https://github.com/qianc62" target="_blank" rel="noopener noreferrer">https://github.com/qianc62</a></td>
|
||||
<td><a href="WareHouse/Website_THUNLP_20230725154612" target="_blank" rel="noopener noreferrer">A simple website</a></td>
|
||||
<td><img src="misc/website.png" width="200px"></td>
|
||||
<td rowspan="5"><a href="https://github.com/THUNLP" target="_blank" rel="noopener noreferrer">https://github.com/THUNLP</a></td>
|
||||
<td><a href="WareHouse/TsinghuaBambooWebsite_THUNLPDemo_2024" target="_blank" rel="noopener noreferrer">Tsinghua Bamboo Website</a></td>
|
||||
<td><img src="misc/tsinghua_bamboo_website.png" width="200px"></td>
|
||||
<td>Perfect</td>
|
||||
<td>GPT3.5</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="WareHouse/tetris_THUNLPDemo_2024" target="_blank" rel="noopener noreferrer">A Tetris Game</a></td>
|
||||
<td><img src="misc/tetris.png" width="200px"></td>
|
||||
<td>Perfect</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="WareHouse/pvz_THUNLPDemo_2024" target="_blank" rel="noopener noreferrer">A Plant vs Zombie Game</a></td>
|
||||
<td><img src="misc/pvz.png" width="200px"></td>
|
||||
<td>Perfect</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="WareHouse/car_THUNLPDemo_2024" target="_blank" rel="noopener noreferrer">A Race Car Game</a></td>
|
||||
<td><img src="misc/car_game.png" width="200px"></td>
|
||||
<td>Perfect</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="WareHouse/snake_THUNLPDemo_2024" target="_blank" rel="noopener noreferrer">A Snake Game</a></td>
|
||||
<td><img src="misc/snake_game.png" width="200px"></td>
|
||||
<td>Perfect</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="4"><a href="https://github.com/qianc62" target="_blank" rel="noopener noreferrer">https://github.com/qianc62</a></td>
|
||||
<td><a href="WareHouse/FlappyBird_THUNLP_20230726121145" target="_blank" rel="noopener noreferrer">FlappyBirds Game</a></td>
|
||||
<td><img src="misc/flappy_bird.png" width="200px"></td>
|
||||
<td>Perfect</td>
|
||||
|
||||
@ -0,0 +1,486 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>ChatDev数字博物馆</title>
|
||||
<!-- CSS -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css">
|
||||
<link rel="stylesheet" href="https://unpkg.com/aos@2.3.1/dist/aos.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Ma+Yen+Kai&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
.bookshelf-container {
|
||||
padding: 50px 20px;
|
||||
background: rgba(244, 241, 234, 0.9);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.bookshelf-row {
|
||||
position: relative;
|
||||
margin-bottom: 60px;
|
||||
height: 270px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
gap: 30px;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.books-track {
|
||||
display: flex;
|
||||
gap: 30px;
|
||||
position: absolute;
|
||||
left: 20px;
|
||||
animation: slideTrack 20s linear infinite;
|
||||
}
|
||||
|
||||
.books-track-reverse {
|
||||
animation: slideTrackReverse 20s linear infinite;
|
||||
}
|
||||
|
||||
.book {
|
||||
width: 180px;
|
||||
height: 270px;
|
||||
flex-shrink: 0;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.book:hover {
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
|
||||
.book-cover {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(135deg, #8B4513, #5C3D2E);
|
||||
border-radius: 5px;
|
||||
border-left: 15px solid #3A2820;
|
||||
box-shadow:
|
||||
-5px 5px 15px rgba(0, 0, 0, 0.3),
|
||||
inset -1px 0 2px rgba(255, 255, 255, 0.1);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 添加书脊纹理 */
|
||||
.book-cover::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -15px;
|
||||
top: 0;
|
||||
width: 15px;
|
||||
height: 100%;
|
||||
background: linear-gradient(to right,
|
||||
rgba(58, 40, 32, 1) 0%,
|
||||
rgba(89, 61, 43, 1) 50%,
|
||||
rgba(58, 40, 32, 1) 100%
|
||||
);
|
||||
box-shadow:
|
||||
inset -1px 0 2px rgba(255, 255, 255, 0.1),
|
||||
inset 1px 0 1px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.book-cover::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -15px;
|
||||
right: 0;
|
||||
height: 3px;
|
||||
background: linear-gradient(to bottom,
|
||||
rgba(255, 255, 255, 0.1) 0%,
|
||||
transparent 100%
|
||||
);
|
||||
}
|
||||
|
||||
.book-image {
|
||||
width: 100%;
|
||||
height: 80%;
|
||||
object-fit: cover;
|
||||
border-radius: 5px 5px 0 0;
|
||||
border-bottom: 2px solid rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.book-title {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
color: #F4E4BC;
|
||||
text-align: center;
|
||||
width: 90%;
|
||||
font-size: 1.1em;
|
||||
font-family: 'Ma Yen Kai', serif;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||
padding: 5px;
|
||||
background: linear-gradient(to bottom,
|
||||
rgba(0, 0, 0, 0) 0%,
|
||||
rgba(0, 0, 0, 0.1) 100%
|
||||
);
|
||||
}
|
||||
|
||||
.book:hover .book-cover {
|
||||
box-shadow:
|
||||
-8px 8px 20px rgba(0, 0, 0, 0.4),
|
||||
inset -1px 0 3px rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.book:hover .book-title {
|
||||
color: #FFE4BC;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
@keyframes slideTrack {
|
||||
0% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(calc(-210px * 6)); /* (180px + 30px) × 6 */
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideTrackReverse {
|
||||
0% {
|
||||
transform: translateX(calc(-210px * 6));
|
||||
}
|
||||
100% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.books-track:hover {
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
text-align: center;
|
||||
font-size: 2.5em;
|
||||
color: #3A2820;
|
||||
margin-bottom: 60px;
|
||||
font-family: 'Ma Yen Kai', serif;
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.bookshelf-row {
|
||||
gap: 20px;
|
||||
}
|
||||
.book {
|
||||
width: 160px;
|
||||
height: 240px;
|
||||
}
|
||||
}
|
||||
|
||||
.modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
z-index: 1000;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.modal.active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
position: relative;
|
||||
max-width: 80%;
|
||||
max-height: 80%;
|
||||
}
|
||||
|
||||
.modal-image {
|
||||
max-width: 100%;
|
||||
max-height: 80vh;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.close-modal {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
color: white;
|
||||
font-size: 30px;
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
left: 0;
|
||||
color: white;
|
||||
font-size: 20px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@keyframes slideBooks {
|
||||
0% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideBooksReverse {
|
||||
0% {
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.books-wrapper:hover {
|
||||
animation-play-state: paused;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="scroll-container">
|
||||
<div class="chinese-scroll">
|
||||
<div class="bamboo-content">
|
||||
<div class="pattern-overlay"></div>
|
||||
|
||||
<header>
|
||||
<nav>
|
||||
<div class="logo">ChatDev数字博物馆</div>
|
||||
<ul>
|
||||
<li><a href="index.html" class="magnetic-link"><span>返回首页</span></a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<div class="bookshelf-container">
|
||||
<h2 class="section-title" data-aos="fade-up">文物考察</h2>
|
||||
|
||||
<div class="bookshelf-row">
|
||||
<div class="books-track">
|
||||
<div class="book">
|
||||
<div class="book-cover">
|
||||
<img src="images/examination/7.png" alt="复原竹简拼图" class="book-image" data-title="复原竹简拼图">
|
||||
<div class="book-title">复原竹简拼图</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="book">
|
||||
<div class="book-cover">
|
||||
<img src="images/examination/8.png" alt="集体会读史料" class="book-image" data-title="集体会读史料">
|
||||
<div class="book-title">集体会读史料</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="book">
|
||||
<div class="book-cover">
|
||||
<img src="images/examination/4.png" alt="解读竹简文脉" class="book-image" data-title="解读竹简文脉">
|
||||
<div class="book-title">解读竹简文脉</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="book">
|
||||
<div class="book-cover">
|
||||
<img src="images/examination/5.png" alt="古文字研究" class="book-image" data-title="古文字研究">
|
||||
<div class="book-title">古文字研究</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="book">
|
||||
<div class="book-cover">
|
||||
<img src="images/examination/1.png" alt="出土文献整理" class="book-image" data-title="出土文献整理">
|
||||
<div class="book-title">出土文献整理</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="book">
|
||||
<div class="book-cover">
|
||||
<img src="images/examination/6.png" alt="清华简史料研讨" class="book-image" data-title="清华简史料研讨">
|
||||
<div class="book-title">清华简史料研讨</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="book">
|
||||
<div class="book-cover">
|
||||
<img src="images/examination/7.png" alt="复原竹简拼图" class="book-image" data-title="复原竹简拼图">
|
||||
<div class="book-title">复原竹简拼图</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="book">
|
||||
<div class="book-cover">
|
||||
<img src="images/examination/8.png" alt="集体会读史料" class="book-image" data-title="集体会读史料">
|
||||
<div class="book-title">集体会读史料</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="book">
|
||||
<div class="book-cover">
|
||||
<img src="images/examination/4.png" alt="解读竹简文脉" class="book-image" data-title="解读竹简文脉">
|
||||
<div class="book-title">解读竹简文脉</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="book">
|
||||
<div class="book-cover">
|
||||
<img src="images/examination/5.png" alt="古文字研究" class="book-image" data-title="古文字研究">
|
||||
<div class="book-title">古文字研究</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="book">
|
||||
<div class="book-cover">
|
||||
<img src="images/examination/1.png" alt="出土文献整理" class="book-image" data-title="出土文献整理">
|
||||
<div class="book-title">出土文献整理</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="book">
|
||||
<div class="book-cover">
|
||||
<img src="images/examination/6.png" alt="清华简史料研讨" class="book-image" data-title="清华简史料研讨">
|
||||
<div class="book-title">清华简史料研讨</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bookshelf-row">
|
||||
<div class="books-track books-track-reverse">
|
||||
<div class="book">
|
||||
<div class="book-cover">
|
||||
<img src="images/examination/3.png" alt="竹简修复技术" class="book-image" data-title="竹简修复技术">
|
||||
<div class="book-title">竹简修复技术</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="book">
|
||||
<div class="book-cover">
|
||||
<img src="images/examination/9.png" alt="挖掘算表奥秘" class="book-image" data-title="挖掘算表奥秘">
|
||||
<div class="book-title">挖掘算表奥秘</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="book">
|
||||
<div class="book-cover">
|
||||
<img src="images/examination/13.png" alt="当代文物传承" class="book-image" data-title="当代文物传承">
|
||||
<div class="book-title">当代文物传承</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="book">
|
||||
<div class="book-cover">
|
||||
<img src="images/examination/14.png" alt="文物成果发布" class="book-image" data-title="文物成果发布">
|
||||
<div class="book-title">文物成果发布</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="book">
|
||||
<div class="book-cover">
|
||||
<img src="images/examination/15.png" alt="文物历史探秘" class="book-image" data-title="文物历史探秘">
|
||||
<div class="book-title">文物历史探秘</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="book">
|
||||
<div class="book-cover">
|
||||
<img src="images/examination/11.png" alt="数字化保护" class="book-image" data-title="数字化保护">
|
||||
<div class="book-title">数字化保护</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="book">
|
||||
<div class="book-cover">
|
||||
<img src="images/examination/3.png" alt="竹简修复技术" class="book-image" data-title="竹简修复技术">
|
||||
<div class="book-title">竹简修复技术</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="book">
|
||||
<div class="book-cover">
|
||||
<img src="images/examination/9.png" alt="挖掘算表奥秘" class="book-image" data-title="挖掘算表奥秘">
|
||||
<div class="book-title">挖掘算表奥秘</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="book">
|
||||
<div class="book-cover">
|
||||
<img src="images/examination/13.png" alt="当代文物传承" class="book-image" data-title="当代文物传承">
|
||||
<div class="book-title">当代文物传承</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="book">
|
||||
<div class="book-cover">
|
||||
<img src="images/examination/14.png" alt="文物成果发布" class="book-image" data-title="文物成果发布">
|
||||
<div class="book-title">文物成果发布</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="book">
|
||||
<div class="book-cover">
|
||||
<img src="images/examination/15.png" alt="文物历史探秘" class="book-image" data-title="文物历史探秘">
|
||||
<div class="book-title">文物历史探秘</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="book">
|
||||
<div class="book-cover">
|
||||
<img src="images/examination/11.png" alt="数字化保护" class="book-image" data-title="数字化保护">
|
||||
<div class="book-title">数字化保护</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-content">
|
||||
<div class="footer-left">
|
||||
<img src="images/logo/chatdev-logo.png" alt="ChatDev Logo" class="footer-logo">
|
||||
<div class="contact" data-aos="fade-up">
|
||||
<p>ChatDev数字博物馆</p>
|
||||
<p>https://github.com/OpenBMB/ChatDev</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="opening-hours" data-aos="fade-up" data-aos-delay="100">
|
||||
<p>以上内容由AI生成,可能存在偏差,请谨慎使用</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal" id="imageModal">
|
||||
<div class="modal-content">
|
||||
<button class="close-modal">×</button>
|
||||
<img src="" alt="" class="modal-image">
|
||||
<div class="modal-title"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollTrigger.min.js"></script>
|
||||
<script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
|
||||
<script>
|
||||
AOS.init({
|
||||
duration: 1000,
|
||||
once: true
|
||||
});
|
||||
|
||||
const modal = document.getElementById('imageModal');
|
||||
const modalImage = modal.querySelector('.modal-image');
|
||||
const modalTitle = modal.querySelector('.modal-title');
|
||||
const closeButton = modal.querySelector('.close-modal');
|
||||
|
||||
document.querySelectorAll('.book-image').forEach(image => {
|
||||
image.addEventListener('click', () => {
|
||||
modalImage.src = image.src;
|
||||
modalTitle.textContent = image.dataset.title;
|
||||
modal.classList.add('active');
|
||||
});
|
||||
});
|
||||
|
||||
closeButton.addEventListener('click', () => {
|
||||
modal.classList.remove('active');
|
||||
});
|
||||
|
||||
modal.addEventListener('click', (e) => {
|
||||
if (e.target === modal) {
|
||||
modal.classList.remove('active');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 442 KiB |
|
After Width: | Height: | Size: 11 MiB |
|
After Width: | Height: | Size: 656 KiB |
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 91 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 305 KiB |
|
After Width: | Height: | Size: 928 KiB |
|
After Width: | Height: | Size: 222 KiB |
|
After Width: | Height: | Size: 124 KiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 486 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 356 KiB |
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 1.9 MiB |
|
After Width: | Height: | Size: 2.8 MiB |
|
After Width: | Height: | Size: 10 MiB |
|
After Width: | Height: | Size: 3.4 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 11 MiB |
|
After Width: | Height: | Size: 7.3 MiB |
|
After Width: | Height: | Size: 4.3 MiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 678 KiB |
|
After Width: | Height: | Size: 319 KiB |
205
WareHouse/TsinghuaBambooWebsite_THUNLPDemo_2024/index.html
Normal file
@ -0,0 +1,205 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>ChatDev数字博物馆</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css">
|
||||
<link rel="stylesheet" href="https://unpkg.com/aos@2.3.1/dist/aos.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/three-dots@0.3.2/dist/three-dots.min.css">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Ma+Yen+Kai&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/chinese-scroll-effect/dist/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="scroll-container">
|
||||
<div class="chinese-scroll">
|
||||
<div class="bamboo-content">
|
||||
<div class="pattern-overlay"></div>
|
||||
|
||||
<header>
|
||||
<nav>
|
||||
<div class="logo">ChatDev数字博物馆</div>
|
||||
<ul>
|
||||
<li><a href="#hero" class="magnetic-link"><span>首页</span></a></li>
|
||||
<li><a href="#cultural-heritage" class="magnetic-link"><span>考古时间线</span></a></li>
|
||||
<li><a href="#exhibitions" class="magnetic-link"><span>研究与保护</span></a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<section id="hero">
|
||||
<div class="swiper hero-slider">
|
||||
<div class="swiper-wrapper">
|
||||
<div class="swiper-slide">
|
||||
<div class="slide-inner">
|
||||
<img src="./images/slider/slide3.jpg" alt="清华简保护">
|
||||
<div class="slide-overlay">
|
||||
<h2>科技保护</h2>
|
||||
<p>运用现代科技保护和研究战国竹简</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="swiper-slide">
|
||||
<div class="slide-inner">
|
||||
<img src="./images/slider/slide1.png" alt="清华简展示">
|
||||
<div class="slide-overlay">
|
||||
<h2>清华简</h2>
|
||||
<p>战国中晚期珍贵竹简,展现先秦古籍原貌</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="swiper-slide">
|
||||
<div class="slide-inner">
|
||||
<img src="./images/slider/slide2.png" alt="清华简研究">
|
||||
<div class="slide-overlay">
|
||||
<h2>文化传承</h2>
|
||||
<p>了解中华文化的初期面貌和发展脉络</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="swiper-pagination"></div>
|
||||
<div class="swiper-button-prev"></div>
|
||||
<div class="swiper-button-next"></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="quote-section" class="parallax-section">
|
||||
<div class="quote-container">
|
||||
<blockquote data-aos="fade-up">
|
||||
<div class="cultural-slogan">
|
||||
<p>"以科技演绎传统,以创新传承文明"</p>
|
||||
</div>
|
||||
</blockquote>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="stats-section">
|
||||
<div class="stats-container">
|
||||
<div class="stat-item" data-aos="fade-up">
|
||||
<div class="stat-number">
|
||||
<span class="counter" data-target="2500">0</span>
|
||||
<span class="plus">+</span>
|
||||
</div>
|
||||
<div class="stat-label">枚竹简</div>
|
||||
<div class="stat-wave"></div>
|
||||
</div>
|
||||
<div class="stat-item" data-aos="fade-up" data-aos-delay="100">
|
||||
<div class="stat-number">
|
||||
<span class="counter" data-target="14">0</span>
|
||||
<span class="plus">+</span>
|
||||
</div>
|
||||
<div class="stat-label">成果整理</div>
|
||||
<div class="stat-wave"></div>
|
||||
</div>
|
||||
<div class="stat-item" data-aos="fade-up" data-aos-delay="200">
|
||||
<div class="stat-number">
|
||||
<span class="counter" data-target="2300">0</span>
|
||||
<span class="plus">+</span>
|
||||
</div>
|
||||
<div class="stat-label">年历史</div>
|
||||
<div class="stat-wave"></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="cultural-heritage">
|
||||
<div class="heritage-container">
|
||||
<h2 class="section-title" data-aos="fade-up">考古时间线</h2>
|
||||
<div class="heritage-timeline">
|
||||
<div class="timeline-item" data-aos="fade-left">
|
||||
<div class="timeline-content">
|
||||
<h3>归来如初,竹简重现</h3>
|
||||
<p>流散海外的战国竹简终归故土,清华大学开启保护与研究之旅,揭开尘封千年的历史画卷。</p>
|
||||
<div class="timeline-period">2008年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="timeline-item" data-aos="fade-right">
|
||||
<div class="timeline-content">
|
||||
<h3>初绽华光,文脉重接</h3>
|
||||
<p>出土文献中心成立,竹简首批成果问世,遗篇重现,周文王遗训与《系年》诉说千年往事。</p>
|
||||
<div class="timeline-period">2009年-2011年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="timeline-item" data-aos="fade-left">
|
||||
<div class="timeline-content">
|
||||
<h3>简中有数,史韵悠长</h3>
|
||||
<p>竹简释读渐入佳境,文献涵盖诗乐、史事与算表,点亮先秦文明的璀璨星河。</p>
|
||||
<div class="timeline-period">2013年-2020年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="timeline-item" data-aos="fade-right">
|
||||
<div class="timeline-content">
|
||||
<h3>弦歌不辍,古韵新生</h3>
|
||||
<p>深掘竹简之秘,释出《五纪》《三不韦》等珍贵篇章,重现礼乐风华,延续文化薪火。</p>
|
||||
<div class="timeline-period">2021年-2023年</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="exhibitions">
|
||||
<h2 class="section-title" data-aos="fade-up">研究与保护</h2>
|
||||
<div class="exhibition-grid">
|
||||
<a href="cultural-examination.html" class="exhibition-card" data-aos="fade-up" data-aos-delay="200" style="text-decoration: none; color: inherit;">
|
||||
<div class="card-image-wrapper">
|
||||
<img src="./images/exhibitions/exhibition1.png" alt="清华简保护">
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<h3>文物考察</h3>
|
||||
<p>对出土文物进行科学清理、保护和研究,确保文物的完整性和真实性</p>
|
||||
</div>
|
||||
</a>
|
||||
<a href="warring-states-bamboo.html" class="exhibition-card" data-aos="fade-up" style="text-decoration: none; color: inherit;">
|
||||
<div class="card-image-wrapper">
|
||||
<img src="./images/exhibitions/exhibition2.png" alt="清华简发现">
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<h3>战国文化</h3>
|
||||
<p>千余枚战国竹简,展现战国文化</p>
|
||||
</div>
|
||||
</a>
|
||||
<a href="research-achievements.html" class="exhibition-card" data-aos="fade-up" data-aos-delay="100" style="text-decoration: none; color: inherit;">
|
||||
<div class="card-image-wrapper">
|
||||
<img src="./images/exhibitions/exhibition3.png" alt="清华简研究">
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<h3>全辑整理</h3>
|
||||
<p>已出版十四辑整理成果,展现先秦文献珍贵价值</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-content">
|
||||
<div class="footer-left">
|
||||
<img src="images/logo/chatdev-logo.png" alt="ChatDev Logo" class="footer-logo">
|
||||
<div class="contact" data-aos="fade-up">
|
||||
<p>ChatDev数字博物馆</p>
|
||||
<p>https://github.com/OpenBMB/ChatDev</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="opening-hours" data-aos="fade-up" data-aos-delay="100">
|
||||
<p>以上内容由AI生成,可能存在偏差,请谨慎使用</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollTrigger.min.js"></script>
|
||||
<script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/vanilla-tilt/1.8.0/vanilla-tilt.min.js"></script>
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,869 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>ChatDev数字博物馆</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css">
|
||||
<link rel="stylesheet" href="https://unpkg.com/aos@2.3.1/dist/aos.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Ma+Yen+Kai&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
.achievements-container {
|
||||
padding: 50px 20px;
|
||||
background: rgba(244, 241, 234, 0.9);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
text-align: center;
|
||||
font-size: 2.5em;
|
||||
color: #4A3728;
|
||||
margin-bottom: 60px;
|
||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.scroll-gallery {
|
||||
position: relative;
|
||||
max-width: 1800px;
|
||||
margin: 0 auto;
|
||||
height: calc(800px * 3);
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
padding: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.scroll-column {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
animation-fill-mode: both;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.scroll-column:nth-child(1) {
|
||||
animation-name: scrollDown;
|
||||
animation-duration: 17.5s;
|
||||
transform: translateY(-40%);
|
||||
}
|
||||
|
||||
.scroll-column:nth-child(2) {
|
||||
animation-name: scrollUp;
|
||||
animation-duration: 22.5s;
|
||||
transform: translateY(-30%);
|
||||
}
|
||||
|
||||
.scroll-column:nth-child(3) {
|
||||
animation-name: scrollDown;
|
||||
animation-duration: 20s;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.scroll-column:nth-child(4) {
|
||||
animation-name: scrollUp;
|
||||
animation-duration: 25s;
|
||||
transform: translateY(-20%);
|
||||
}
|
||||
|
||||
.scroll-column:nth-child(5) {
|
||||
animation-name: scrollDown;
|
||||
animation-duration: 19s;
|
||||
transform: translateY(-35%);
|
||||
}
|
||||
|
||||
@keyframes scrollUp {
|
||||
0% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scrollDown {
|
||||
0% {
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-column:hover {
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
.scroll-item {
|
||||
position: relative;
|
||||
height: 800px;
|
||||
min-width: 250px;
|
||||
background: #FFF;
|
||||
border-radius: 15px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
|
||||
transition: all 0.4s ease;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.scroll-item:hover {
|
||||
transform: translateY(-10px);
|
||||
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.2);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.scroll-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: transform 0.4s ease;
|
||||
}
|
||||
|
||||
.scroll-item:hover .scroll-image {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.scroll-overlay {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: linear-gradient(to top, rgba(0, 0, 0, 0.8), transparent);
|
||||
padding: 20px;
|
||||
color: #FFF;
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
transition: all 0.4s ease;
|
||||
}
|
||||
|
||||
.scroll-item:hover .scroll-overlay {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.scroll-title {
|
||||
font-size: 1.4em;
|
||||
margin-bottom: 10px;
|
||||
color: #F4E4BC;
|
||||
}
|
||||
|
||||
.scroll-date {
|
||||
font-size: 0.9em;
|
||||
color: #DEB887;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.expanded-view {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.95);
|
||||
z-index: 1000;
|
||||
display: none;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
transition: opacity 0.5s ease;
|
||||
}
|
||||
|
||||
.expanded-view.active {
|
||||
display: flex;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.expanded-content {
|
||||
position: relative;
|
||||
width: 90%;
|
||||
max-width: 1200px;
|
||||
height: 90%;
|
||||
display: flex;
|
||||
gap: 30px;
|
||||
color: #FFF;
|
||||
transform: scale(0.9);
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
|
||||
.expanded-view.active .expanded-content {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
.expanded-image {
|
||||
flex: 1;
|
||||
max-width: 70%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.expanded-info {
|
||||
flex: 0 0 30%;
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.close-expanded {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
color: #FFF;
|
||||
font-size: 30px;
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
border: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.scroll-item:nth-child(3n+1),
|
||||
.scroll-item:nth-child(3n+2),
|
||||
.scroll-item:nth-child(3n+3) {
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.scroll-gallery {
|
||||
gap: 15px;
|
||||
height: calc(700px * 3);
|
||||
}
|
||||
.scroll-item {
|
||||
height: 700px;
|
||||
min-width: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.scroll-gallery {
|
||||
gap: 10px;
|
||||
height: calc(600px * 3);
|
||||
}
|
||||
.scroll-item {
|
||||
height: 600px;
|
||||
min-width: 150px;
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-indicator {
|
||||
position: fixed;
|
||||
right: 20px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.indicator-dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
background: rgba(139, 69, 19, 0.3);
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.indicator-dot.active {
|
||||
background: #8B4513;
|
||||
transform: scale(1.5);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="scroll-container">
|
||||
<div class="chinese-scroll">
|
||||
<div class="bamboo-content">
|
||||
<div class="pattern-overlay"></div>
|
||||
|
||||
<header>
|
||||
<nav>
|
||||
<div class="logo">ChatDev数字博物馆</div>
|
||||
<ul>
|
||||
<li><a href="index.html" class="magnetic-link"><span>返回首页</span></a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<div class="achievements-container">
|
||||
<h2 class="section-title" data-aos="fade-up">全辑整理</h2>
|
||||
|
||||
<div class="scroll-gallery">
|
||||
<div class="scroll-column">
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/1.png" alt="清华简第一辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第一辑</h3>
|
||||
<div class="scroll-date">2010年12月</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/2.png" alt="清华简第二辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第二辑</h3>
|
||||
<div class="scroll-date">2011年12月</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/3.png" alt="清华简第三辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第三辑</h3>
|
||||
<div class="scroll-date">2012年12月</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/4.png" alt="清华简第四辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第四辑</h3>
|
||||
<div class="scroll-date">2013年12月</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/5.png" alt="清华简第五辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第五辑</h3>
|
||||
<div class="scroll-date">2015年4月</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/6.png" alt="清华简第六辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第六辑</h3>
|
||||
<div class="scroll-date">2016年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/7.png" alt="清华简第七辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第七辑</h3>
|
||||
<div class="scroll-date">2017年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/8.png" alt="清华简第八辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第八辑</h3>
|
||||
<div class="scroll-date">2018年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/9.png" alt="清华简第九辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第九辑</h3>
|
||||
<div class="scroll-date">2019年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/10.png" alt="清华简第十辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第十辑</h3>
|
||||
<div class="scroll-date">2020年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/11.png" alt="清华简第十一辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第十一辑</h3>
|
||||
<div class="scroll-date">2021年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/12.png" alt="清华简第十二辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第十二辑</h3>
|
||||
<div class="scroll-date">2022年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/13.png" alt="清华简第十三辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第十三辑</h3>
|
||||
<div class="scroll-date">2022年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/14.png" alt="清华简第十四辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第十四辑</h3>
|
||||
<div class="scroll-date">2023年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/15.png" alt="清华简第一辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第一辑</h3>
|
||||
<div class="scroll-date">2010年12月</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="scroll-column">
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/14.png" alt="清华简第二辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第二辑</h3>
|
||||
<div class="scroll-date">2011年12月</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/13.png" alt="清华简第三辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第三辑</h3>
|
||||
<div class="scroll-date">2012年12月</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/12.png" alt="清华简第四辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第四辑</h3>
|
||||
<div class="scroll-date">2013年12月</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/11.png" alt="清华简第五辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第五辑</h3>
|
||||
<div class="scroll-date">2015年4月</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/10.png" alt="清华简第六辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第六辑</h3>
|
||||
<div class="scroll-date">2016年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/9.png" alt="清华简第七辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第七辑</h3>
|
||||
<div class="scroll-date">2017年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/8.png" alt="清华简第八辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第八辑</h3>
|
||||
<div class="scroll-date">2018年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/7.png" alt="清华简第九辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第九辑</h3>
|
||||
<div class="scroll-date">2019年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/6.png" alt="清华简第十辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第十辑</h3>
|
||||
<div class="scroll-date">2020年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/5.png" alt="清华简第十一辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第十一辑</h3>
|
||||
<div class="scroll-date">2021年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/4.png" alt="清华简第十二辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第十二辑</h3>
|
||||
<div class="scroll-date">2022年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/3.png" alt="清华简第十三辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第十三辑</h3>
|
||||
<div class="scroll-date">2022年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/2.png" alt="清华简第十四辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第十四辑</h3>
|
||||
<div class="scroll-date">2023年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/1.png" alt="清华简第二辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第二辑</h3>
|
||||
<div class="scroll-date">2011年12月</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="scroll-column">
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/2.png" alt="清华简第三辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第三辑</h3>
|
||||
<div class="scroll-date">2012年12月</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/3.png" alt="清华简第四辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第四辑</h3>
|
||||
<div class="scroll-date">2013年12月</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/4.png" alt="清华简第五辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第五辑</h3>
|
||||
<div class="scroll-date">2015年4月</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/5.png" alt="清华简第六辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第六辑</h3>
|
||||
<div class="scroll-date">2016年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/6.png" alt="清华简第七辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第七辑</h3>
|
||||
<div class="scroll-date">2017年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/7.png" alt="清华简第八辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第八辑</h3>
|
||||
<div class="scroll-date">2018年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/8.png" alt="清华简第九辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第九辑</h3>
|
||||
<div class="scroll-date">2019年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/9.png" alt="清华简第十辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第十辑</h3>
|
||||
<div class="scroll-date">2020年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/10.png" alt="清华简第十一辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第十一辑</h3>
|
||||
<div class="scroll-date">2021年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/11.png" alt="清华简第十二辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第十二辑</h3>
|
||||
<div class="scroll-date">2022年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/12.png" alt="清华简第十三辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第十三辑</h3>
|
||||
<div class="scroll-date">2022年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/13.png" alt="清华简第十四辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第十四辑</h3>
|
||||
<div class="scroll-date">2023年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/14.png" alt="清华简第三辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第三辑</h3>
|
||||
<div class="scroll-date">2012年12月</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="scroll-column">
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/15.png" alt="清华简第四辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第四辑</h3>
|
||||
<div class="scroll-date">2013年12月</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/14.png" alt="清华简第五辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第五辑</h3>
|
||||
<div class="scroll-date">2015年4月</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/13.png" alt="清华简第六辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第六辑</h3>
|
||||
<div class="scroll-date">2016年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/12.png" alt="清华简第七辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第七辑</h3>
|
||||
<div class="scroll-date">2017年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/11.png" alt="清华简第八辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第八辑</h3>
|
||||
<div class="scroll-date">2018年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/10.png" alt="清华简第九辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第九辑</h3>
|
||||
<div class="scroll-date">2019年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/9.png" alt="清华简第十辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第十辑</h3>
|
||||
<div class="scroll-date">2020年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/8.png" alt="清华简第十一辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第十一辑</h3>
|
||||
<div class="scroll-date">2021年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/7.png" alt="清华简第十二辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第十二辑</h3>
|
||||
<div class="scroll-date">2022年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/6.png" alt="清华简第十三辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第十三辑</h3>
|
||||
<div class="scroll-date">2022年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/5.png" alt="清华简第十四辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第十四辑</h3>
|
||||
<div class="scroll-date">2023年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/4.png" alt="清华简第四辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第四辑</h3>
|
||||
<div class="scroll-date">2013年12月</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="scroll-column">
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/3.png" alt="清华简第五辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第五辑</h3>
|
||||
<div class="scroll-date">2015年4月</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/2.png" alt="清华简第六辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第六辑</h3>
|
||||
<div class="scroll-date">2016年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/1.png" alt="清华简第七辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第七辑</h3>
|
||||
<div class="scroll-date">2017年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/2.png" alt="清华简第八辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第八辑</h3>
|
||||
<div class="scroll-date">2018年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/3.png" alt="清华简第九辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第九辑</h3>
|
||||
<div class="scroll-date">2019年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/4.png" alt="清华简第十辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第十辑</h3>
|
||||
<div class="scroll-date">2020年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/5.png" alt="清华简第十一辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第十一辑</h3>
|
||||
<div class="scroll-date">2021年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/6.png" alt="清华简第十二辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第十二辑</h3>
|
||||
<div class="scroll-date">2022年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/7.png" alt="清华简第十三辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第十三辑</h3>
|
||||
<div class="scroll-date">2022年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/8.png" alt="清华简第十四辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第十四辑</h3>
|
||||
<div class="scroll-date">2023年</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-item">
|
||||
<img src="images/research-achievements/9.png" alt="清华简第五辑" class="scroll-image">
|
||||
<div class="scroll-overlay">
|
||||
<h3 class="scroll-title">《清华简》第五辑</h3>
|
||||
<div class="scroll-date">2015年4月</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-content">
|
||||
<div class="footer-left">
|
||||
<img src="images/logo/chatdev-logo.png" alt="ChatDev Logo" class="footer-logo">
|
||||
<div class="contact" data-aos="fade-up">
|
||||
<p>ChatDev数字博物馆</p>
|
||||
<p>https://github.com/OpenBMB/ChatDev</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="opening-hours" data-aos="fade-up" data-aos-delay="100">
|
||||
<p>以上内容由AI生成,可能存在偏差,请谨慎使用</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="expanded-view">
|
||||
<button class="close-expanded">×</button>
|
||||
<div class="expanded-content">
|
||||
<img src="" alt="" class="expanded-image">
|
||||
<div class="expanded-info">
|
||||
<h2 class="expanded-title"></h2>
|
||||
<div class="expanded-date"></div>
|
||||
<div class="expanded-description"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
|
||||
<script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
|
||||
<script>
|
||||
AOS.init({
|
||||
duration: 1000,
|
||||
once: true
|
||||
});
|
||||
|
||||
const scrollItems = document.querySelectorAll('.scroll-item');
|
||||
const expandedView = document.querySelector('.expanded-view');
|
||||
const expandedImage = expandedView.querySelector('.expanded-image');
|
||||
const expandedTitle = expandedView.querySelector('.expanded-title');
|
||||
const expandedDate = expandedView.querySelector('.expanded-date');
|
||||
const expandedDescription = expandedView.querySelector('.expanded-description');
|
||||
const closeExpanded = expandedView.querySelector('.close-expanded');
|
||||
|
||||
const descriptions = [
|
||||
"收录《周武王有疾周公所自以代王之志》《皇门》等重要文献为研究周代历史提供新材料。",
|
||||
"包含《系年》等重要历史文献,为战国时期编年史研究提供重要参考。",
|
||||
"收录《赵简子》《祭公》等文献,展现先秦时期政治制度与礼仪文化。",
|
||||
"包含《郑文公》《克殷》等篇章,丰富了我们对商周之际历史的认识。",
|
||||
"收录《湯處》《厚父》等重要文献,展现商周时期的政治思想。",
|
||||
"包含多篇重要历史文献,进一步丰富了先秦历史研究资料。",
|
||||
"展现了战国时期的政治、文化等多个方面的珍贵史料。",
|
||||
"记载了大量先秦时期的重要历史事件和文化现象。",
|
||||
"为研究先秦历史提供了新的文献依据和研究视角。",
|
||||
"展现了战国时期的文化特征和历史发展脉络。",
|
||||
"记录了大量珍贵的历史资料和文化遗产。",
|
||||
"为研究先秦历史提供了重要的文献支持。",
|
||||
"包含多篇重要的历史文献和文化记载。",
|
||||
"展现了战国时期丰富的历史文化内涵。"
|
||||
];
|
||||
|
||||
scrollItems.forEach((item, index) => {
|
||||
item.addEventListener('click', () => {
|
||||
const image = item.querySelector('.scroll-image');
|
||||
const title = item.querySelector('.scroll-title');
|
||||
const date = item.querySelector('.scroll-date');
|
||||
|
||||
expandedImage.src = image.src.replace('600x800', '1200x1600');
|
||||
expandedTitle.textContent = title.textContent;
|
||||
expandedDate.textContent = date.textContent;
|
||||
const randomIndex = Math.floor(Math.random() * descriptions.length);
|
||||
expandedDescription.textContent = descriptions[randomIndex];
|
||||
|
||||
expandedView.classList.add('active');
|
||||
document.body.style.overflow = 'hidden';
|
||||
});
|
||||
});
|
||||
|
||||
closeExpanded.addEventListener('click', () => {
|
||||
expandedView.classList.remove('active');
|
||||
document.body.style.overflow = '';
|
||||
});
|
||||
|
||||
expandedView.addEventListener('click', (e) => {
|
||||
if (e.target === expandedView) {
|
||||
expandedView.classList.remove('active');
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape' && expandedView.classList.contains('active')) {
|
||||
expandedView.classList.remove('active');
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
216
WareHouse/TsinghuaBambooWebsite_THUNLPDemo_2024/script.js
Normal file
@ -0,0 +1,216 @@
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
AOS.init({
|
||||
duration: 1000,
|
||||
once: true,
|
||||
offset: 100,
|
||||
disable: '.parallax-section' // Disable AOS for parallax section
|
||||
});
|
||||
|
||||
const swiper = new Swiper('.hero-slider', {
|
||||
loop: true,
|
||||
speed: 1000,
|
||||
autoplay: {
|
||||
delay: 5000,
|
||||
disableOnInteraction: false,
|
||||
},
|
||||
pagination: {
|
||||
el: '.swiper-pagination',
|
||||
clickable: true,
|
||||
},
|
||||
navigation: {
|
||||
nextEl: '.swiper-button-next',
|
||||
prevEl: '.swiper-button-prev',
|
||||
},
|
||||
});
|
||||
|
||||
const magneticLinks = document.querySelectorAll('.magnetic-link');
|
||||
|
||||
magneticLinks.forEach(link => {
|
||||
link.addEventListener('mousemove', function(e) {
|
||||
const bounds = this.getBoundingClientRect();
|
||||
const mouseX = e.clientX - bounds.left;
|
||||
const mouseY = e.clientY - bounds.top;
|
||||
const centerX = bounds.width / 2;
|
||||
const centerY = bounds.height / 2;
|
||||
const deltaX = mouseX - centerX;
|
||||
const deltaY = mouseY - centerY;
|
||||
|
||||
gsap.to(this, {
|
||||
x: deltaX * 0.3,
|
||||
y: deltaY * 0.3,
|
||||
duration: 0.3
|
||||
});
|
||||
});
|
||||
|
||||
link.addEventListener('mouseleave', function() {
|
||||
gsap.to(this, {
|
||||
x: 0,
|
||||
y: 0,
|
||||
duration: 0.3
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
|
||||
const timelineItems = document.querySelectorAll('.timeline-item');
|
||||
timelineItems.forEach((item, index) => {
|
||||
const tl = gsap.timeline({
|
||||
scrollTrigger: {
|
||||
trigger: item,
|
||||
start: "top center+=100",
|
||||
end: "bottom center",
|
||||
toggleActions: "play none none reverse"
|
||||
}
|
||||
});
|
||||
|
||||
tl.from(item.querySelector('::before'), {
|
||||
scale: 0,
|
||||
opacity: 0,
|
||||
duration: 0.6,
|
||||
ease: "back.out(1.7)"
|
||||
});
|
||||
|
||||
tl.from(item.querySelector('.timeline-content'), {
|
||||
x: index % 2 === 0 ? 50 : -50,
|
||||
opacity: 0,
|
||||
duration: 0.8,
|
||||
ease: "power2.out"
|
||||
}, "-=0.3");
|
||||
|
||||
tl.from(item.querySelector('.timeline-period'), {
|
||||
y: 20,
|
||||
opacity: 0,
|
||||
duration: 0.5,
|
||||
ease: "power2.out"
|
||||
}, "-=0.4");
|
||||
});
|
||||
|
||||
VanillaTilt.init(document.querySelectorAll('.digital-card'), {
|
||||
max: 15,
|
||||
speed: 400,
|
||||
glare: true,
|
||||
'max-glare': 0.2
|
||||
});
|
||||
|
||||
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
||||
anchor.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
const target = document.querySelector(this.getAttribute('href'));
|
||||
if (target) {
|
||||
window.scrollTo({
|
||||
top: target.offsetTop - 70,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const patternOverlay = document.querySelector('.pattern-overlay');
|
||||
window.addEventListener('mousemove', (e) => {
|
||||
const moveX = (e.clientX - window.innerWidth / 2) * 0.01;
|
||||
const moveY = (e.clientY - window.innerHeight / 2) * 0.01;
|
||||
gsap.to(patternOverlay, {
|
||||
x: moveX,
|
||||
y: moveY,
|
||||
duration: 1,
|
||||
ease: 'power2.out'
|
||||
});
|
||||
});
|
||||
|
||||
const exhibitionCards = document.querySelectorAll('.exhibition-card');
|
||||
exhibitionCards.forEach(card => {
|
||||
card.addEventListener('mouseenter', () => {
|
||||
gsap.to(card, {
|
||||
y: -10,
|
||||
scale: 1.02,
|
||||
duration: 0.3,
|
||||
ease: 'power2.out'
|
||||
});
|
||||
});
|
||||
|
||||
card.addEventListener('mouseleave', () => {
|
||||
gsap.to(card, {
|
||||
y: 0,
|
||||
scale: 1,
|
||||
duration: 0.3,
|
||||
ease: 'power2.out'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function createParticles() {
|
||||
const particleContainer = document.createElement('div');
|
||||
particleContainer.className = 'particle-container';
|
||||
document.body.appendChild(particleContainer);
|
||||
|
||||
for (let i = 0; i < 50; i++) {
|
||||
const particle = document.createElement('div');
|
||||
particle.className = 'particle';
|
||||
particle.style.left = Math.random() * 100 + 'vw';
|
||||
particle.style.animationDelay = Math.random() * 5 + 's';
|
||||
particleContainer.appendChild(particle);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
createParticles();
|
||||
|
||||
const scrollElements = document.querySelectorAll('.bamboo-content > *');
|
||||
scrollElements.forEach(element => {
|
||||
element.style.opacity = '0';
|
||||
element.style.transform = 'translateX(-20px)';
|
||||
});
|
||||
|
||||
const revealOnScroll = () => {
|
||||
scrollElements.forEach(element => {
|
||||
const elementTop = element.getBoundingClientRect().top;
|
||||
if (elementTop < window.innerHeight - 100) {
|
||||
element.style.opacity = '1';
|
||||
element.style.transform = 'translateX(0)';
|
||||
element.style.transition = 'all 0.8s ease';
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', revealOnScroll);
|
||||
revealOnScroll();
|
||||
});
|
||||
|
||||
function animateCounter(element) {
|
||||
const target = parseInt(element.dataset.target);
|
||||
const duration = 2000;
|
||||
const step = target / (duration / 16); // 60fps
|
||||
let current = 0;
|
||||
|
||||
const updateCounter = () => {
|
||||
current += step;
|
||||
if (current < target) {
|
||||
element.textContent = Math.floor(current);
|
||||
requestAnimationFrame(updateCounter);
|
||||
} else {
|
||||
element.textContent = target;
|
||||
}
|
||||
};
|
||||
|
||||
updateCounter();
|
||||
}
|
||||
|
||||
const observerOptions = {
|
||||
threshold: 0.5
|
||||
};
|
||||
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
const counters = entry.target.querySelectorAll('.counter');
|
||||
counters.forEach(counter => animateCounter(counter));
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
}, observerOptions);
|
||||
|
||||
document.querySelectorAll('.stats-section').forEach(section => {
|
||||
observer.observe(section);
|
||||
});
|
||||
});
|
||||
1158
WareHouse/TsinghuaBambooWebsite_THUNLPDemo_2024/styles.css
Normal file
@ -0,0 +1,369 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>ChatDev数字博物馆</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css">
|
||||
<link rel="stylesheet" href="https://unpkg.com/aos@2.3.1/dist/aos.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Ma+Yen+Kai&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
.bamboo-container {
|
||||
padding: 50px 20px;
|
||||
background: rgba(244, 241, 234, 0.9);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
text-align: center;
|
||||
font-size: 2.5em;
|
||||
color: #4A3728;
|
||||
margin-bottom: 60px;
|
||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.gallery-container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.gallery-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
|
||||
gap: 40px;
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
|
||||
.gallery-item {
|
||||
position: relative;
|
||||
background: #DEB887;
|
||||
border-radius: 15px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.gallery-item:hover {
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
|
||||
.gallery-image {
|
||||
width: 100%;
|
||||
height: 500px;
|
||||
object-fit: cover;
|
||||
border-radius: 15px 15px 0 0;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.gallery-item:hover .gallery-image {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.gallery-overlay {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: linear-gradient(to top, rgba(89, 44, 12, 0.95), transparent);
|
||||
padding: 30px;
|
||||
color: #FFFFFF;
|
||||
transform: translateY(70%);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.gallery-item:hover .gallery-overlay {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.gallery-title {
|
||||
font-size: 1.8em;
|
||||
margin-bottom: 15px;
|
||||
color: #FFE4B5;
|
||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.gallery-description {
|
||||
font-size: 1.1em;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 20px;
|
||||
opacity: 1;
|
||||
color: #FFFFFF;
|
||||
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.gallery-details {
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
margin-top: 15px;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
.gallery-details ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.gallery-details li {
|
||||
margin-bottom: 8px;
|
||||
padding-left: 20px;
|
||||
position: relative;
|
||||
color: #FFE4B5;
|
||||
}
|
||||
|
||||
.gallery-details li::before {
|
||||
content: '•';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
color: #FFE4B5;
|
||||
}
|
||||
|
||||
.featured-section {
|
||||
position: relative;
|
||||
height: 600px;
|
||||
margin-bottom: 60px;
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.featured-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.featured-content {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 40px;
|
||||
background: linear-gradient(to top, rgba(89, 44, 12, 0.95), transparent);
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.featured-title {
|
||||
font-size: 2.5em;
|
||||
margin-bottom: 20px;
|
||||
color: #FFE4B5;
|
||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.featured-description {
|
||||
font-size: 1.2em;
|
||||
line-height: 1.8;
|
||||
max-width: 800px;
|
||||
color: #FFFFFF;
|
||||
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.5);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Modal styles */
|
||||
.modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
z-index: 1000;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transition: background-color 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
.modal.active {
|
||||
display: flex;
|
||||
background-color: rgba(0, 0, 0, 0.9);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
position: relative;
|
||||
max-width: 90%;
|
||||
max-height: 90vh;
|
||||
}
|
||||
|
||||
.modal-image {
|
||||
max-width: 100%;
|
||||
max-height: 90vh;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.close-modal {
|
||||
position: absolute;
|
||||
top: -40px;
|
||||
right: 0;
|
||||
color: white;
|
||||
font-size: 30px;
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.gallery-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.gallery-image {
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.featured-section {
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.featured-title {
|
||||
font-size: 2em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="scroll-container">
|
||||
<div class="chinese-scroll">
|
||||
<div class="bamboo-content">
|
||||
<!-- Chinese Pattern Overlay -->
|
||||
<div class="pattern-overlay"></div>
|
||||
|
||||
<header>
|
||||
<nav>
|
||||
<div class="logo">ChatDev数字博物馆</div>
|
||||
<ul>
|
||||
<li><a href="index.html" class="magnetic-link"><span>返回首页</span></a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<div class="bamboo-container">
|
||||
<h2 class="section-title" data-aos="fade-up">战国文化</h2>
|
||||
|
||||
<div class="gallery-container">
|
||||
<div class="featured-section" data-aos="fade-up">
|
||||
<img src="./images/warring-states-bamboo-1.png" alt="清华简" class="featured-image">
|
||||
<div class="featured-content">
|
||||
<h2 class="featured-title">清华简 - 战国时期的文化瑰宝</h2>
|
||||
<p class="featured-description">2008年入藏清华大学的2500余枚战国竹简,是迄今为止最重要的先秦文献发现之一,为研究中国古代历史提供了珍贵的第一手资料。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gallery-grid">
|
||||
<div class="gallery-item" data-aos="fade-up">
|
||||
<img src="./images/warring-states-bamboo-sub1.png" alt="礼仪记录" class="gallery-image">
|
||||
<div class="gallery-overlay">
|
||||
<h3 class="gallery-title">礼仪记录</h3>
|
||||
<p class="gallery-description">两篇文献编连成卷,竹简长约46厘米,宽约0.6厘米,详细记载了楚国大夫饮食礼仪制度。</p>
|
||||
<div class="gallery-details">
|
||||
<ul>
|
||||
<li>《大夫食礼》51支简,《大夫食礼记》14支简</li>
|
||||
<li>简背有刻画痕迹,各简均有编号</li>
|
||||
<li>前者记载食礼内容,后者论述执事者职责</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gallery-item" data-aos="fade-up" data-aos-delay="100">
|
||||
<img src="./images/warring-states-bamboo-sub2.png" alt="八卦图" class="gallery-image">
|
||||
<div class="gallery-overlay">
|
||||
<h3 class="gallery-title">八卦占卜</h3>
|
||||
<p class="gallery-description">战国时期楚国独特的占筮方法,是目前所见最早的《易》图。</p>
|
||||
<div class="gallery-details">
|
||||
<ul>
|
||||
<li>保存完好,维持原来成卷状态</li>
|
||||
<li>分栏书写,附有插图和表格</li>
|
||||
<li>首次展现八卦分置八方的卦位图</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gallery-item" data-aos="fade-up" data-aos-delay="200">
|
||||
<img src="./images/warring-states-bamboo-sub3.png" alt="算表" class="gallery-image">
|
||||
<div class="gallery-overlay">
|
||||
<h3 class="gallery-title">数学算表</h3>
|
||||
<p class="gallery-description">形成于公元前305年左右的古代数学文献,比里耶秦简九九表更早。</p>
|
||||
<div class="gallery-details">
|
||||
<ul>
|
||||
<li>21支竹简构成21行20列的计算表</li>
|
||||
<li>可用于两位数乘除法运算</li>
|
||||
<li>支持含分数1/2的两位数乘法</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-content">
|
||||
<div class="footer-left">
|
||||
<img src="images/logo/chatdev-logo.png" alt="ChatDev Logo" class="footer-logo">
|
||||
<div class="contact" data-aos="fade-up">
|
||||
<p>ChatDev数字博物馆</p>
|
||||
<p>https://github.com/OpenBMB/ChatDev</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="opening-hours" data-aos="fade-up" data-aos-delay="100">
|
||||
<p>以上内容由AI生成,可能存在偏差,请谨慎使用</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal" id="imageModal">
|
||||
<div class="modal-content">
|
||||
<button class="close-modal">×</button>
|
||||
<img src="" alt="" class="modal-image">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
|
||||
<script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
|
||||
<script>
|
||||
AOS.init({
|
||||
duration: 1000,
|
||||
once: true
|
||||
});
|
||||
|
||||
const modal = document.getElementById('imageModal');
|
||||
const modalImage = modal.querySelector('.modal-image');
|
||||
const closeButton = modal.querySelector('.close-modal');
|
||||
const images = document.querySelectorAll('.gallery-image, .featured-image');
|
||||
|
||||
images.forEach(image => {
|
||||
image.addEventListener('click', () => {
|
||||
modalImage.src = image.src.replace('600x800', '1200x1600').replace('1400x600', '2800x1200');
|
||||
modal.classList.add('active');
|
||||
});
|
||||
});
|
||||
|
||||
closeButton.addEventListener('click', () => {
|
||||
modal.classList.remove('active');
|
||||
});
|
||||
|
||||
modal.addEventListener('click', (e) => {
|
||||
if (e.target === modal) {
|
||||
modal.classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape' && modal.classList.contains('active')) {
|
||||
modal.classList.remove('active');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
90
WareHouse/car_THUNLPDemo_2024/ChatChainConfig.json
Normal file
@ -0,0 +1,90 @@
|
||||
{
|
||||
"chain": [
|
||||
{
|
||||
"phase": "Coding",
|
||||
"phaseType": "SimplePhase",
|
||||
"max_turn_step": 1,
|
||||
"need_reflect": "False"
|
||||
},
|
||||
{
|
||||
"phase": "CodeCompleteAll",
|
||||
"phaseType": "ComposedPhase",
|
||||
"cycleNum": 10,
|
||||
"Composition": [{
|
||||
"phase": "CodeComplete",
|
||||
"phaseType": "SimplePhase",
|
||||
"max_turn_step": 1,
|
||||
"need_reflect": "False"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"phase": "CodeReview",
|
||||
"phaseType": "ComposedPhase",
|
||||
"cycleNum": 2,
|
||||
"Composition": [{
|
||||
"phase": "CodeReviewComment",
|
||||
"phaseType": "SimplePhase",
|
||||
"max_turn_step": 1,
|
||||
"need_reflect": "False"
|
||||
},
|
||||
{
|
||||
"phase": "CodeReviewModification",
|
||||
"phaseType": "SimplePhase",
|
||||
"max_turn_step": 1,
|
||||
"need_reflect": "False"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"phase": "Test",
|
||||
"phaseType": "ComposedPhase",
|
||||
"cycleNum": 2,
|
||||
"Composition": [
|
||||
{
|
||||
"phase": "TestErrorSummary",
|
||||
"phaseType": "SimplePhase",
|
||||
"max_turn_step": 1,
|
||||
"need_reflect": "False"
|
||||
},
|
||||
{
|
||||
"phase": "TestModification",
|
||||
"phaseType": "SimplePhase",
|
||||
"max_turn_step": 1,
|
||||
"need_reflect": "False"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"phase": "HumanAgentInteraction",
|
||||
"phaseType": "ComposedPhase",
|
||||
"cycleNum": 1,
|
||||
"Composition": [
|
||||
{
|
||||
"phase": "CodeReviewHuman",
|
||||
"phaseType": "SimplePhase",
|
||||
"max_turn_step": 2,
|
||||
"need_reflect": "False"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"recruitments": [
|
||||
"Chief Executive Officer",
|
||||
"Counselor",
|
||||
"Chief Human Resource Officer",
|
||||
"Chief Product Officer",
|
||||
"Chief Technology Officer",
|
||||
"Programmer",
|
||||
"Code Reviewer",
|
||||
"Software Test Engineer",
|
||||
"Chief Creative Officer"
|
||||
],
|
||||
"clear_structure": "True",
|
||||
"gui_design": "True",
|
||||
"git_management": "False",
|
||||
"web_spider": "False",
|
||||
"self_improve": "False",
|
||||
"incremental_develop": "False",
|
||||
"with_memory": "False",
|
||||
"background_prompt": "ChatDev is a virtual LLM Agent software company aiming to build the best software with simple but robust code, maximizing the controllability and visual effect of the software in pure python."
|
||||
}
|
||||
1
WareHouse/car_THUNLPDemo_2024/Demo.prompt
Normal file
@ -0,0 +1 @@
|
||||
仔细开发一个赛车小游戏
|
||||
306
WareHouse/car_THUNLPDemo_2024/PhaseConfig.json
Normal file
@ -0,0 +1,306 @@
|
||||
{
|
||||
"DemandAnalysis": {
|
||||
"assistant_role_name": "Chief Product Officer",
|
||||
"user_role_name": "Chief Executive Officer",
|
||||
"phase_prompt": [
|
||||
"ChatDev has made products in the following form before:",
|
||||
"Image: can present information in line chart, bar chart, flow chart, cloud chart, Gantt chart, etc.",
|
||||
"Document: can present information via .docx files.",
|
||||
"PowerPoint: can present information via .pptx files.",
|
||||
"Excel: can present information via .xlsx files.",
|
||||
"PDF: can present information via .pdf files.",
|
||||
"Website: can present personal resume, tutorial, products, or ideas, via .html files.",
|
||||
"Application: can implement visualized game, software, tool, etc, via python.",
|
||||
"Dashboard: can display a panel visualizing real-time information.",
|
||||
"Mind Map: can represent ideas, with related concepts arranged around a core concept.",
|
||||
"As the {assistant_role}, to satisfy the new user's demand and the product should be realizable, you should keep discussing with me to decide which product modality do we want the product to be?",
|
||||
"Note that we must ONLY discuss the product modality and do not discuss anything else! Once we all have expressed our opinion(s) and agree with the results of the discussion unanimously, any of us must actively terminate the discussion by replying with only one line, which starts with a single word <INFO>, followed by our final product modality without any other words, e.g., \"<INFO> PowerPoint\"."
|
||||
]
|
||||
},
|
||||
"LanguageChoose": {
|
||||
"assistant_role_name": "Chief Technology Officer",
|
||||
"user_role_name": "Chief Executive Officer",
|
||||
"phase_prompt": [
|
||||
"According to the new user's task and some creative brainstorm ideas listed below: ",
|
||||
"Task: \"{task}\".",
|
||||
"Modality: \"{modality}\".",
|
||||
"Ideas: \"{ideas}\".",
|
||||
"We have decided to complete the task through a executable software implemented via a programming language. ",
|
||||
"As the {assistant_role}, to satisfy the new user's demand and make the software realizable, you should propose a concrete programming language. If python can complete this task via Python, please answer Python; otherwise, answer another programming language (e.g., Java, C++, etc,).",
|
||||
"Note that we must ONLY discuss the target programming language and do not discuss anything else! Once we all have expressed our opinion(s) and agree with the results of the discussion unanimously, any of us must actively terminate the discussion and conclude the best programming language we have discussed without any other words or reasons, return only one line using the format: \"<INFO> *\" where \"*\" represents a programming language."
|
||||
]
|
||||
},
|
||||
"Coding": {
|
||||
"assistant_role_name": "Programmer",
|
||||
"user_role_name": "Chief Technology Officer",
|
||||
"phase_prompt": [
|
||||
"According to the new user's task and our software designs listed below: ",
|
||||
"Task: \"{task}\".",
|
||||
"Task description: \"{description}\".",
|
||||
"Modality: \"{modality}\".",
|
||||
"Programming Language: \"{language}\"",
|
||||
"Ideas:\"{ideas}\"",
|
||||
"We have decided to complete the task through a executable software with multiple files implemented via {language}. As the {assistant_role}, to satisfy the new user's demands, you should write one or multiple files and make sure that every detail of the architecture is, in the end, implemented as code. {gui}",
|
||||
"Think step by step and reason yourself to the right decisions to make sure we get it right.",
|
||||
"If you are using python to develop a software, develop it all in pure python, do not use assets such as images, videos, etc. The animation and GUI are necessary but should all implement in pure python.",
|
||||
"You will first lay out the names of the core classes, functions, methods that will be necessary, as well as a quick comment on their purpose.",
|
||||
"Then you will output the content of each file including complete code. Each file must strictly follow a markdown code block format, where the following tokens must be replaced such that \"FILENAME\" is the lowercase file name including the file extension, \"LANGUAGE\" in the programming language, \"DOCSTRING\" is a string literal specified in source code that is used to document a specific segment of code, and \"CODE\" is the original code:",
|
||||
"FILENAME",
|
||||
"```LANGUAGE",
|
||||
"'''",
|
||||
"DOCSTRING",
|
||||
"'''",
|
||||
"CODE",
|
||||
"```",
|
||||
"You will start with the \"main\" file, then go to the ones that are imported by that file, and so on.",
|
||||
"Please note that the code should be fully functional. Ensure to implement all functions. No placeholders (such as 'pass' in Python)."
|
||||
]
|
||||
},
|
||||
"ArtDesign": {
|
||||
"assistant_role_name": "Programmer",
|
||||
"user_role_name": "Chief Creative Officer",
|
||||
"phase_prompt": [
|
||||
"Our developed source codes and corresponding test reports are listed below: ",
|
||||
"Task: \"{task}\".",
|
||||
"Programming Language: \"{language}\"",
|
||||
"Source Codes:",
|
||||
"\"{codes}\"",
|
||||
"Note that each file must strictly follow a markdown code block format, where the following tokens must be replaced such that \"FILENAME\" is the lowercase file name including the file extension, \"LANGUAGE\" in the programming language, \"DOCSTRING\" is a string literal specified in source code that is used to document a specific segment of code, and \"CODE\" is the original code:",
|
||||
"FILENAME",
|
||||
"```LANGUAGE",
|
||||
"'''",
|
||||
"DOCSTRING",
|
||||
"'''",
|
||||
"CODE",
|
||||
"```",
|
||||
"As the {assistant_role}, to satisfy the new user's demand and equip the software with a beautiful graphical user interface (GUI), we will discuss and design many decorative images for GUI decoration. Now, we keep discussing the GUI beautification by listing some functionally independent elements in GUI that are being considered to be decorated by different pictures. For example, ten digits (0-9) in a calculator are functionally independent.",
|
||||
"To answer, use the format: \" FILENAME.png: DESCRIPTION\" where \"FILENAME\" is the filename of the image and \"DESCRIPTION\" denotes the detailed description of the independent elements. For example:",
|
||||
"'''",
|
||||
"button_1.png: The button with the number \"1\" on it.",
|
||||
"button_multiply.png: The button with the multiplication symbol (\"*\") on it.",
|
||||
"background.png: the background color to decorate the Go game",
|
||||
"'''",
|
||||
"Now, list all functionally independent elements as much as possible."
|
||||
]
|
||||
},
|
||||
"ArtIntegration": {
|
||||
"assistant_role_name": "Programmer",
|
||||
"user_role_name": "Chief Creative Officer",
|
||||
"phase_prompt": [
|
||||
"Our developed source codes and corresponding test reports are listed below: ",
|
||||
"Task: \"{task}\".",
|
||||
"Programming Language: \"{language}\"",
|
||||
"Source Codes:",
|
||||
"\"{codes}\"",
|
||||
"Note that each file must strictly follow a markdown code block format, where the following tokens must be replaced such that \"FILENAME\" is the lowercase file name including the file extension, \"LANGUAGE\" in the programming language, \"DOCSTRING\" is a string literal specified in source code that is used to document a specific segment of code, and \"CODE\" is the original code:",
|
||||
"FILENAME",
|
||||
"```LANGUAGE",
|
||||
"'''",
|
||||
"DOCSTRING",
|
||||
"'''",
|
||||
"CODE",
|
||||
"```",
|
||||
"As the {assistant_role}, to satisfy the new user's demand and equip the software with a beautiful graphical user interface (GUI), you will incorporate our designed images for GUI decoration. Here are some ready-made high-quality pictures and corresponding descriptions:",
|
||||
"{images}",
|
||||
"Note that the designed images have a fixed size of 256x256 pixels and the images are located in the same directory as all the Python files; please dynamically scaling these images according to the size of GUI, and use \"self.*\" to avoid displaying-related problems caused by automatic garbage collection. For example:",
|
||||
"```",
|
||||
"self.image = ImageTk.PhotoImage(Image.open(\"./image.png\").resize((50, 50)))",
|
||||
"```",
|
||||
"Now, use some or all of the pictures into the GUI to make it more beautiful and creative. Output codes strictly following the required format mentioned above."
|
||||
]
|
||||
},
|
||||
"CodeComplete": {
|
||||
"assistant_role_name": "Programmer",
|
||||
"user_role_name": "Chief Technology Officer",
|
||||
"phase_prompt": [
|
||||
"According to the new user's task and our software designs listed below: ",
|
||||
"Task: \"{task}\".",
|
||||
"Modality: \"{modality}\".",
|
||||
"Programming Language: \"{language}\"",
|
||||
"Codes:",
|
||||
"\"{codes}\"",
|
||||
"Unimplemented File:",
|
||||
"\"{unimplemented_file}\"",
|
||||
"If you are using python to develop a software, develop it all in pure python, do not use assets such as images, videos, etc. The animation and GUI are necessary but should all implement in pure python.",
|
||||
"In our software, each file must strictly follow a markdown code block format, where the following tokens must be replaced such that \"FILENAME\" is the lowercase file name including the file extension, \"LANGUAGE\" in the programming language, \"DOCSTRING\" is a string literal specified in source code that is used to document a specific segment of code, and \"CODE\" is the original code:",
|
||||
"FILENAME",
|
||||
"```LANGUAGE",
|
||||
"'''",
|
||||
"DOCSTRING",
|
||||
"'''",
|
||||
"CODE",
|
||||
"```",
|
||||
"As the {assistant_role}, to satisfy the complete function of our developed software, you have to implement all methods in the {unimplemented_file} file which contains a unimplemented class. Now, implement all methods of the {unimplemented_file} and all other codes needed, then output the fully implemented codes, strictly following the required format."
|
||||
]
|
||||
},
|
||||
"CodeReviewComment": {
|
||||
"assistant_role_name": "Code Reviewer",
|
||||
"user_role_name": "Programmer",
|
||||
"phase_prompt": [
|
||||
"According to the new user's task and our software designs: ",
|
||||
"Task: \"{task}\".",
|
||||
"Modality: \"{modality}\".",
|
||||
"Programming Language: \"{language}\"",
|
||||
"Ideas: \"{ideas}\"",
|
||||
"Codes:",
|
||||
"\"{codes}\"",
|
||||
"As the {assistant_role}, to make the software directly operable without further coding, ChatDev have formulated the following regulations:",
|
||||
"1) all referenced classes should be imported;",
|
||||
"2) all methods should be implemented;",
|
||||
"3) all methods need to have the necessary comments;",
|
||||
"4) no potential bugs;",
|
||||
"5) The entire project conforms to the tasks proposed by the user;",
|
||||
"6) most importantly, do not only check the errors in the code, but also the logic of code. Make sure that user can interact with generated software without losing any feature in the requirement;",
|
||||
"7) If you are using python to develop a software, develop it all in pure python, do not use assets such as images, videos, etc. The animation and GUI are necessary but should all implement in pure python.",
|
||||
"Now, you should check the above regulations one by one and review the codes in detail, propose one comment with the highest priority about the codes, and give me instructions on how to fix. Tell me your comment with the highest priority and corresponding suggestions on revision. If the codes are perfect and you have no comment on them, return only one line like \"<INFO> Finished\"."
|
||||
]
|
||||
},
|
||||
"CodeReviewHuman": {
|
||||
"assistant_role_name": "Programmer",
|
||||
"user_role_name": "Code Reviewer",
|
||||
"phase_prompt": [
|
||||
"According to the new user's task, our designed product modality and three creative ideas, our developed first-edition source codes are listed below: ",
|
||||
"Task: \"{task}\".",
|
||||
"Modality: \"{modality}\".",
|
||||
"Programming Language: \"{language}\"",
|
||||
"Ideas: \"{ideas}\"",
|
||||
"Codes: ",
|
||||
"\"{codes}\"",
|
||||
"Comments on Codes:",
|
||||
"\"{comments}\"",
|
||||
"In the software, each file must strictly follow a markdown code block format, where the following tokens must be replaced such that \"FILENAME\" is the lowercase file name including the file extension, \"LANGUAGE\" in the programming language, \"DOCSTRING\" is a string literal specified in source code that is used to document a specific segment of code, and \"CODE\" is the original code. Format:",
|
||||
"FILENAME",
|
||||
"```LANGUAGE",
|
||||
"'''",
|
||||
"DOCSTRING",
|
||||
"'''",
|
||||
"CODE",
|
||||
"```",
|
||||
"As the {assistant_role}, to satisfy the new user's demand and make the software creative, executive and robust, you should modify corresponding codes according to the comments. Then, output the full and complete codes with all bugs fixed based on the comments. Return all codes strictly following the required format."
|
||||
]
|
||||
},
|
||||
"CodeReviewModification": {
|
||||
"assistant_role_name": "Programmer",
|
||||
"user_role_name": "Code Reviewer",
|
||||
"phase_prompt": [
|
||||
"According to the new user's task, our designed product modality, languages and ideas, our developed first-edition source codes are listed below: ",
|
||||
"Task: \"{task}\".",
|
||||
"Modality: \"{modality}\".",
|
||||
"Programming Language: \"{language}\"",
|
||||
"Ideas: \"{ideas}\"",
|
||||
"Codes: ",
|
||||
"\"{codes}\"",
|
||||
"Comments on Codes:",
|
||||
"\"{comments}\"",
|
||||
"If you are using python to develop a software, develop it all in pure python, do not use assets such as images, videos, etc. The animation and GUI are necessary but should all implement in pure python.",
|
||||
"In the software, each file must strictly follow a markdown code block format, where the following tokens must be replaced such that \"FILENAME\" is the lowercase file name including the file extension, \"LANGUAGE\" in the programming language, \"DOCSTRING\" is a string literal specified in source code that is used to document a specific segment of code, and \"CODE\" is the original code. Format:",
|
||||
"FILENAME",
|
||||
"```LANGUAGE",
|
||||
"'''",
|
||||
"DOCSTRING",
|
||||
"'''",
|
||||
"CODE",
|
||||
"```",
|
||||
"As the {assistant_role}, to satisfy the new user's demand and make the software creative, executive and robust, you should modify corresponding codes according to the comments. Then, output the full and complete codes with all bugs fixed based on the comments. Return all codes strictly following the required format."
|
||||
]
|
||||
},
|
||||
"TestErrorSummary": {
|
||||
"assistant_role_name": "Programmer",
|
||||
"user_role_name": "Software Test Engineer",
|
||||
"phase_prompt": [
|
||||
"Our developed source codes and corresponding test reports are listed below: ",
|
||||
"Programming Language: \"{language}\"",
|
||||
"Source Codes:",
|
||||
"\"{codes}\"",
|
||||
"Test Reports of Source Codes:",
|
||||
"\"{test_reports}\"",
|
||||
"According to my test reports, please locate and summarize the bugs that cause the problem."
|
||||
]
|
||||
},
|
||||
"TestModification": {
|
||||
"assistant_role_name": "Programmer",
|
||||
"user_role_name": "Software Test Engineer",
|
||||
"phase_prompt": [
|
||||
"Our developed source codes and corresponding test reports are listed below: ",
|
||||
"Programming Language: \"{language}\"",
|
||||
"Source Codes:",
|
||||
"\"{codes}\"",
|
||||
"Test Reports of Source Codes:",
|
||||
"\"{test_reports}\"",
|
||||
"Error Summary of Test Reports:",
|
||||
"\"{error_summary}\"",
|
||||
"Note that each file must strictly follow a markdown code block format, where the following tokens must be replaced such that \"FILENAME\" is the lowercase file name including the file extension, \"LANGUAGE\" in the programming language, \"DOCSTRING\" is a string literal specified in source code that is used to document a specific segment of code, and \"CODE\" is the original code:",
|
||||
"FILENAME",
|
||||
"```LANGUAGE",
|
||||
"'''",
|
||||
"DOCSTRING",
|
||||
"'''",
|
||||
"CODE",
|
||||
"```",
|
||||
"As the {assistant_role}, to satisfy the new user's demand and make the software execute smoothly and robustly, you should modify the codes based on the error summary. Now, use the format exemplified above and modify the problematic codes based on the error summary. Output the codes that you fixed based on the test reported and corresponding explanations (strictly follow the format defined above, including FILENAME, LANGUAGE, DOCSTRING and CODE; incomplete \"TODO\" codes are strictly prohibited). If no bugs are reported, please return only one line like \"<INFO> Finished\"."
|
||||
]
|
||||
},
|
||||
"EnvironmentDoc": {
|
||||
"assistant_role_name": "Programmer",
|
||||
"user_role_name": "Chief Technology Officer",
|
||||
"phase_prompt": [
|
||||
"The new user's task and our developed codes are listed: ",
|
||||
"Task: \"{task}\".",
|
||||
"Modality: \"{modality}\".",
|
||||
"Programming Language: \"{language}\"",
|
||||
"Ideas: \"{ideas}\"",
|
||||
"Codes: ",
|
||||
"\"{codes}\"",
|
||||
"As the {assistant_role}, you should write a requirements.txt file, which is commonly used in Python projects to specify the dependencies or packages required for the project to run properly. It serves as a way to document and manage the project's dependencies in a standardized format. For example:",
|
||||
"requirements.txt",
|
||||
"```",
|
||||
"numpy==1.19.2",
|
||||
"pandas>=1.1.4",
|
||||
"```",
|
||||
"According to the codes and file format listed above, write a requirements.txt file to specify the dependencies or packages required for the project to run properly."
|
||||
]
|
||||
},
|
||||
"Manual": {
|
||||
"assistant_role_name": "Chief Product Officer",
|
||||
"user_role_name": "Chief Executive Officer",
|
||||
"phase_prompt": [
|
||||
"The new user's task, our developed codes and required dependencies are listed: ",
|
||||
"Task: \"{task}\".",
|
||||
"Modality: \"{modality}\".",
|
||||
"Programming Language: \"{language}\"",
|
||||
"Ideas: \"{ideas}\"",
|
||||
"Codes: ",
|
||||
"\"{codes}\"",
|
||||
"Requirements:",
|
||||
"\"{requirements}\"",
|
||||
"As the {assistant_role}, by using Markdown, you should write a manual.md file which is a detailed user manual to use the software, including introducing main functions of the software, how to install environment dependencies and how to use/play it. For example:",
|
||||
"manual.md",
|
||||
"```",
|
||||
"# LangChain",
|
||||
"Building applications with LLMs through composability",
|
||||
"Looking for the JS/TS version? Check out LangChain.js.",
|
||||
"**Production Support:** As you move your LangChains into production, we'd love to offer more comprehensive support.",
|
||||
"Please fill out this form and we'll set up a dedicated support Slack channel.",
|
||||
"## Quick Install",
|
||||
"`pip install langchain`",
|
||||
"or",
|
||||
"`conda install langchain -c conda-forge`",
|
||||
"## 🤔 What is this?",
|
||||
"Large language models (LLMs) are emerging as a transformative technology, enabling developers to build applications that they previously could not. However, using these LLMs in isolation is often insufficient for creating a truly powerful app - the real power comes when you can combine them with other sources of computation or knowledge.",
|
||||
"This library aims to assist in the development of those types of applications. Common examples of these applications include:",
|
||||
"**❓ Question Answering over specific documents**",
|
||||
"- Documentation",
|
||||
"- End-to-end Example: Question Answering over Notion Database",
|
||||
"**🤖 Agents**",
|
||||
"- Documentation",
|
||||
"- End-to-end Example: GPT+WolframAlpha",
|
||||
"## 📖 Documentation",
|
||||
"Please see [here](https://python.langchain.com) for full documentation on:",
|
||||
"- Getting started (installation, setting up the environment, simple examples)",
|
||||
"- How-To examples (demos, integrations, helper functions)",
|
||||
"- Reference (full API docs)",
|
||||
"- Resources (high-level explanation of core concepts)",
|
||||
"```"
|
||||
]
|
||||
}
|
||||
}
|
||||
65
WareHouse/car_THUNLPDemo_2024/RoleConfig.json
Normal file
@ -0,0 +1,65 @@
|
||||
{
|
||||
"Chief Executive Officer": [
|
||||
"{chatdev_prompt}",
|
||||
"You are Chief Executive Officer. Now, we are both working at ChatDev and we share a common interest in collaborating to successfully complete a task assigned by a new customer.",
|
||||
"Your main responsibilities include being an active decision-maker on users' demands and other key policy issues, leader, manager, and executor. Your decision-making role involves high-level decisions about policy and strategy; and your communicator role can involve speaking to the organization's management and employees.",
|
||||
"Here is a new customer's task: {task}.",
|
||||
"To complete the task, I will give you one or more instructions, and you must help me to write a specific solution that appropriately solves the requested instruction based on your expertise and my needs."
|
||||
],
|
||||
"Chief Product Officer": [
|
||||
"{chatdev_prompt}",
|
||||
"You are Chief Product Officer. we are both working at ChatDev. We share a common interest in collaborating to successfully complete a task assigned by a new customer.",
|
||||
"You are responsible for all product-related matters in ChatDev. Usually includes product design, product strategy, product vision, product innovation, project management and product marketing.",
|
||||
"Here is a new customer's task: {task}.",
|
||||
"To complete the task, you must write a response that appropriately solves the requested instruction based on your expertise and customer's needs."
|
||||
],
|
||||
"Counselor": [
|
||||
"{chatdev_prompt}",
|
||||
"You are Counselor. Now, we share a common interest in collaborating to successfully complete a task assigned by a new customer.",
|
||||
"Your main responsibilities include asking what user and customer think and provide your valuable suggestions. ",
|
||||
"Here is a new customer's task: {task}.",
|
||||
"To complete the task, I will give you one or more instructions, and you must help me to write a specific solution that appropriately solves the requested instruction based on your expertise and my needs."
|
||||
],
|
||||
"Chief Technology Officer": [
|
||||
"{chatdev_prompt}",
|
||||
"You are Chief Technology Officer. we are both working at ChatDev. We share a common interest in collaborating to successfully complete a task assigned by a new customer.",
|
||||
"You are very familiar to information technology. You will make high-level decisions for the overarching technology infrastructure that closely align with the organization's goals, while you work alongside the organization's information technology (\"IT\") staff members to perform everyday operations.",
|
||||
"Here is a new customer's task: {task}.",
|
||||
"To complete the task, You must write a response that appropriately solves the requested instruction based on your expertise and customer's needs."
|
||||
],
|
||||
"Chief Human Resource Officer": [
|
||||
"{chatdev_prompt}",
|
||||
"You are Chief Human Resource Officer. Now, we are both working at ChatDev and we share a common interest in collaborating to successfully complete a task assigned by a new customer.",
|
||||
"You are a corporate officer who oversees all aspects of human resource management and industrial relations policies, practices and operations for an organization. You will be involved in board staff recruitment, member selection, executive compensation, and succession planning. Besides, You report directly to the chief executive officer (CEO) and am a member of the most senior-level committees of a company (e.g., executive committee or office of CEO).",
|
||||
"Here is a new customer's task: {task}.",
|
||||
"To complete the task, you must write a response that appropriately solves the requested instruction based on your expertise and customer's needs."
|
||||
],
|
||||
"Programmer": [
|
||||
"{chatdev_prompt}",
|
||||
"You are Programmer. we are both working at ChatDev. We share a common interest in collaborating to successfully complete a task assigned by a new customer.",
|
||||
"You can write/create computer software or applications by providing a specific programming language to the computer. You have extensive computing and coding experience in many varieties of programming languages and platforms, such as Python, Java, C, C++, HTML, CSS, JavaScript, XML, SQL, PHP, etc,.",
|
||||
"Here is a new customer's task: {task}.",
|
||||
"To complete the task, you must write a response that appropriately solves the requested instruction based on your expertise and customer's needs."
|
||||
],
|
||||
"Code Reviewer": [
|
||||
"{chatdev_prompt}",
|
||||
"You are Code Reviewer. we are both working at ChatDev. We share a common interest in collaborating to successfully complete a task assigned by a new customer.",
|
||||
"You can help programmers to assess source codes for software troubleshooting, fix bugs to increase code quality and robustness, and offer proposals to improve the source codes.",
|
||||
"Here is a new customer's task: {task}.",
|
||||
"To complete the task, you must write a response that appropriately solves the requested instruction based on your expertise and customer's needs."
|
||||
],
|
||||
"Software Test Engineer": [
|
||||
"{chatdev_prompt}",
|
||||
"You are Software Test Engineer. we are both working at ChatDev. We share a common interest in collaborating to successfully complete a task assigned by a new customer.",
|
||||
"You can use the software as intended to analyze its functional properties, design manual and automated test procedures to evaluate each software product, build and implement software evaluation test programs, and run test programs to ensure that testing protocols evaluate the software correctly.",
|
||||
"Here is a new customer's task: {task}.",
|
||||
"To complete the task, you must write a response that appropriately solves the requested instruction based on your expertise and customer's needs."
|
||||
],
|
||||
"Chief Creative Officer": [
|
||||
"{chatdev_prompt}",
|
||||
"You are Chief Creative Officer. we are both working at ChatDev. We share a common interest in collaborating to successfully complete a task assigned by a new customer.",
|
||||
"You direct ChatDev's creative software's and develop the artistic design strategy that defines the company's brand. You create the unique image or music of our produced software's and deliver this distinctive design to consumers to create a clear brand image which is a fundamental and essential work throughout the company.",
|
||||
"Here is a new customer's task: {task}.",
|
||||
"To complete the task, you must write a response that appropriately solves the requested instruction based on your expertise and customer's needs."
|
||||
]
|
||||
}
|
||||
37
WareHouse/car_THUNLPDemo_2024/car.py
Normal file
@ -0,0 +1,37 @@
|
||||
'''
|
||||
Player car class implementation with improved visual representation and road boundary constraints
|
||||
'''
|
||||
import pygame
|
||||
class PlayerCar:
|
||||
def __init__(self, x, y):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.width = 40
|
||||
self.height = 60
|
||||
self.speed = 5
|
||||
self.color = (255, 0, 0) # Red color
|
||||
def handle_input(self, keys, screen_width):
|
||||
# Road boundaries
|
||||
left_boundary = screen_width//3 + 20 # Add padding for visibility
|
||||
right_boundary = 2*screen_width//3 # Subtract padding for visibility
|
||||
if keys[pygame.K_LEFT] and self.x > left_boundary:
|
||||
self.x -= self.speed
|
||||
if keys[pygame.K_RIGHT] and self.x < right_boundary - self.width:
|
||||
self.x += self.speed
|
||||
if keys[pygame.K_UP] and self.y > 0:
|
||||
self.y -= self.speed
|
||||
if keys[pygame.K_DOWN] and self.y < 600 - self.height:
|
||||
self.y += self.speed
|
||||
def update(self):
|
||||
pass
|
||||
def draw(self, screen):
|
||||
# Draw car body
|
||||
pygame.draw.rect(screen, self.color, (self.x, self.y, self.width, self.height))
|
||||
# Draw car details (wheels and windows)
|
||||
pygame.draw.rect(screen, (0, 0, 0), (self.x + 5, self.y + 10, 10, 40)) # Left wheel
|
||||
pygame.draw.rect(screen, (0, 0, 0), (self.x + 25, self.y + 10, 10, 40)) # Right wheel
|
||||
pygame.draw.rect(screen, (200, 200, 200), (self.x + 5, self.y + 5, 30, 15)) # Windshield
|
||||
def check_collision(self, other):
|
||||
return pygame.Rect(self.x, self.y, self.width, self.height).colliderect(
|
||||
pygame.Rect(other.x, other.y, other.width, other.height)
|
||||
)
|
||||
30
WareHouse/car_THUNLPDemo_2024/gameobjects.py
Normal file
@ -0,0 +1,30 @@
|
||||
'''
|
||||
Game objects implementation including opponent cars with improved visuals and road constraints
|
||||
'''
|
||||
import pygame
|
||||
import random
|
||||
class OpponentCar:
|
||||
def __init__(self, screen_width):
|
||||
self.width = 40
|
||||
self.height = 60
|
||||
self.screen_width = screen_width
|
||||
self.road_left = screen_width//3 + 20
|
||||
self.road_right = 2*screen_width//3
|
||||
self.reset_position()
|
||||
self.color = (0, 0, 255) # Blue color
|
||||
self.speed = random.randint(3, 7)
|
||||
def reset_position(self):
|
||||
# Ensure opponents spawn within road boundaries
|
||||
self.x = random.randint(self.road_left, self.road_right - self.width)
|
||||
self.y = random.randint(-300, -100)
|
||||
def update(self):
|
||||
self.y += self.speed
|
||||
if self.y > 600:
|
||||
self.reset_position()
|
||||
def draw(self, screen):
|
||||
# Draw car body
|
||||
pygame.draw.rect(screen, self.color, (self.x, self.y, self.width, self.height))
|
||||
# Draw car details
|
||||
pygame.draw.rect(screen, (0, 0, 0), (self.x + 5, self.y + 10, 10, 40)) # Left wheel
|
||||
pygame.draw.rect(screen, (0, 0, 0), (self.x + 25, self.y + 10, 10, 40)) # Right wheel
|
||||
pygame.draw.rect(screen, (200, 200, 200), (self.x + 5, self.y + 5, 30, 15)) # Windshield
|
||||
76
WareHouse/car_THUNLPDemo_2024/main.py
Normal file
@ -0,0 +1,76 @@
|
||||
'''
|
||||
Main game file that initializes the racing game and runs the game loop
|
||||
'''
|
||||
import pygame
|
||||
import sys
|
||||
from car import PlayerCar
|
||||
from track import Track
|
||||
from gameobjects import OpponentCar
|
||||
class RacingGame:
|
||||
def __init__(self):
|
||||
pygame.init()
|
||||
self.width = 800
|
||||
self.height = 600
|
||||
self.screen = pygame.display.set_mode((self.width, self.height))
|
||||
pygame.display.set_caption("赛车游戏")
|
||||
self.clock = pygame.time.Clock()
|
||||
self.running = True
|
||||
self.game_over = False
|
||||
self.font = pygame.font.Font(None, 74)
|
||||
self.reset_game()
|
||||
def reset_game(self):
|
||||
# Initialize player at the center of the road
|
||||
road_center = (self.width//3 + (2*self.width//3 + 20)) // 2
|
||||
self.player = PlayerCar(road_center - 20, self.height - 100)
|
||||
self.track = Track(self.width, self.height)
|
||||
self.opponents = [OpponentCar(self.width) for _ in range(3)]
|
||||
self.score = 0
|
||||
self.game_over = False
|
||||
def handle_events(self):
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
self.running = False
|
||||
if event.type == pygame.KEYDOWN and self.game_over:
|
||||
if event.key == pygame.K_SPACE:
|
||||
self.reset_game()
|
||||
if not self.game_over:
|
||||
keys = pygame.key.get_pressed()
|
||||
self.player.handle_input(keys, self.width)
|
||||
def update(self):
|
||||
if not self.game_over:
|
||||
self.player.update()
|
||||
self.track.update()
|
||||
for opponent in self.opponents:
|
||||
opponent.update()
|
||||
if self.player.check_collision(opponent):
|
||||
self.game_over = True
|
||||
self.score += 1
|
||||
def render(self):
|
||||
self.screen.fill((100, 100, 100)) # Gray background
|
||||
self.track.draw(self.screen)
|
||||
self.player.draw(self.screen)
|
||||
for opponent in self.opponents:
|
||||
opponent.draw(self.screen)
|
||||
# Draw score
|
||||
font = pygame.font.Font(None, 36)
|
||||
score_text = font.render(f'Score: {self.score}', True, (255, 255, 255))
|
||||
self.screen.blit(score_text, (10, 10))
|
||||
if self.game_over:
|
||||
game_over_text = self.font.render('GAME OVER', True, (255, 0, 0))
|
||||
restart_text = pygame.font.Font(None, 36).render('Press SPACE to restart', True, (255, 255, 255))
|
||||
text_rect = game_over_text.get_rect(center=(self.width//2, self.height//2))
|
||||
restart_rect = restart_text.get_rect(center=(self.width//2, self.height//2 + 50))
|
||||
self.screen.blit(game_over_text, text_rect)
|
||||
self.screen.blit(restart_text, restart_rect)
|
||||
pygame.display.flip()
|
||||
def run(self):
|
||||
while self.running:
|
||||
self.handle_events()
|
||||
self.update()
|
||||
self.render()
|
||||
self.clock.tick(60)
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
if __name__ == "__main__":
|
||||
game = RacingGame()
|
||||
game.run()
|
||||
33
WareHouse/car_THUNLPDemo_2024/meta.txt
Normal file
@ -0,0 +1,33 @@
|
||||
Task:
|
||||
仔细开发一个赛车小游戏
|
||||
|
||||
Config:
|
||||
ChatEnvConfig.with_memory: False
|
||||
ChatEnvConfig.clear_structure: True
|
||||
ChatEnvConfig.git_management: False
|
||||
ChatEnvConfig.gui_design: True
|
||||
ChatEnvConfig.incremental_develop: False
|
||||
ChatEnvConfig.background_prompt: ChatDev is a virtual LLM Agent software company aiming to build the best software with simple but robust code, maximizing the controllability and visual effect of the software in pure python.
|
||||
|
||||
|
||||
Roster:
|
||||
Chief Executive Officer, Counselor, Chief Human Resource Officer, Chief Product Officer, Chief Technology Officer, Programmer, Code Reviewer, Software Test Engineer, Chief Creative Officer
|
||||
|
||||
Modality:
|
||||
website or app, prefer app
|
||||
|
||||
Ideas:
|
||||
|
||||
|
||||
Language:
|
||||
python for app, or html/css/js for website, prefer app
|
||||
|
||||
Code_Version:
|
||||
3.0
|
||||
|
||||
Proposed_images:
|
||||
0
|
||||
|
||||
Incorporated_images:
|
||||
0
|
||||
|
||||
31
WareHouse/car_THUNLPDemo_2024/track.py
Normal file
@ -0,0 +1,31 @@
|
||||
'''
|
||||
Track class for managing the racing environment with improved visuals and clear boundaries
|
||||
'''
|
||||
import pygame
|
||||
class Track:
|
||||
def __init__(self, width, height):
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.line_spacing = 100
|
||||
self.line_height = 30
|
||||
self.line_speed = 5
|
||||
self.lines = []
|
||||
self.initialize_lines()
|
||||
self.road_left = self.width//3
|
||||
self.road_right = 2*self.width//3 + 20
|
||||
def initialize_lines(self):
|
||||
for y in range(0, self.height, self.line_spacing):
|
||||
self.lines.append(y)
|
||||
def update(self):
|
||||
self.lines = [(y + self.line_speed) % self.height for y in self.lines]
|
||||
def draw(self, screen):
|
||||
# Draw road background
|
||||
pygame.draw.rect(screen, (50, 50, 50),
|
||||
(self.road_left, 0, self.road_right - self.road_left, self.height))
|
||||
# Draw road borders
|
||||
pygame.draw.rect(screen, (255, 255, 0), (self.road_left - 5, 0, 5, self.height)) # Left border
|
||||
pygame.draw.rect(screen, (255, 255, 0), (self.road_right, 0, 5, self.height)) # Right border
|
||||
# Draw road lines
|
||||
for y in self.lines:
|
||||
pygame.draw.rect(screen, (255, 255, 255),
|
||||
(self.width//2 - 10, y, 20, self.line_height))
|
||||
BIN
WareHouse/pvz_THUNLPDemo_2024/assets/music/bgm.mp3
Normal file
63
WareHouse/pvz_THUNLPDemo_2024/constants.py
Normal file
@ -0,0 +1,63 @@
|
||||
import pygame
|
||||
from enum import Enum
|
||||
|
||||
# Window Settings
|
||||
WINDOW_WIDTH = 1080
|
||||
WINDOW_HEIGHT = 900
|
||||
CELL_SIZE = 100
|
||||
GRID_ROWS = 7
|
||||
GRID_COLS = 9
|
||||
TOP_MARGIN = 100
|
||||
FPS = 60
|
||||
|
||||
# Colors
|
||||
WHITE = (255, 255, 255)
|
||||
BLACK = (0, 0, 0)
|
||||
GREEN = (0, 255, 0)
|
||||
DARK_GREEN = (0, 100, 0)
|
||||
BROWN = (139, 69, 19)
|
||||
YELLOW = (255, 255, 0)
|
||||
LAWN_GREEN = (124, 252, 0)
|
||||
GRAY = (128, 128, 128)
|
||||
RED = (255, 0, 0)
|
||||
|
||||
# Game States
|
||||
class GameState(Enum):
|
||||
MENU = 1
|
||||
PLAYING = 2
|
||||
PAUSED = 3
|
||||
GAME_OVER = 4
|
||||
|
||||
# Plant Types
|
||||
class PlantType(Enum):
|
||||
SUNFLOWER = 1
|
||||
PEASHOOTER = 2
|
||||
ROSE_SHOOTER = 3
|
||||
CHOMPER = 4
|
||||
SNOW_PEA = 5
|
||||
|
||||
# Zombie Types
|
||||
class ZombieType(Enum):
|
||||
NORMAL = 1
|
||||
CONE = 2
|
||||
BUCKET = 3
|
||||
NEWSPAPER = 4
|
||||
DANCING = 5
|
||||
|
||||
# Plant Stats
|
||||
PLANT_STATS = {
|
||||
PlantType.SUNFLOWER: {"health": 100, "cost": 50, "color": YELLOW},
|
||||
PlantType.PEASHOOTER: {"health": 120, "cost": 100, "color": GREEN},
|
||||
PlantType.ROSE_SHOOTER: {"health": 140, "cost": 150, "color": (255, 192, 203)},
|
||||
PlantType.CHOMPER: {"health": 180, "cost": 175, "color": (148, 0, 211)},
|
||||
PlantType.SNOW_PEA: {"health": 130, "cost": 175, "color": (0, 191, 255)}
|
||||
}
|
||||
|
||||
# Zombie Stats
|
||||
ZOMBIE_STATS = {
|
||||
ZombieType.NORMAL: {"health": 300, "speed": 0.4, "damage": 0.4},
|
||||
ZombieType.CONE: {"health": 450, "speed": 0.45, "damage": 0.3},
|
||||
ZombieType.BUCKET: {"health": 550, "speed": 0.35, "damage": 0.5},
|
||||
ZombieType.NEWSPAPER: {"health": 200, "speed": 0.6, "damage": 0.3},
|
||||
ZombieType.DANCING: {"health": 350, "speed": 0.5, "damage": 0.35}
|
||||
}
|
||||
285
WareHouse/pvz_THUNLPDemo_2024/entities.py
Normal file
@ -0,0 +1,285 @@
|
||||
import pygame
|
||||
import math
|
||||
from constants import *
|
||||
from sprites import PLANT_DRAWINGS, ZOMBIE_DRAWINGS
|
||||
|
||||
class Plant:
|
||||
def __init__(self, x, y, plant_type):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.type = plant_type
|
||||
stats = PLANT_STATS[plant_type]
|
||||
self.health = stats["health"]
|
||||
self.max_health = stats["health"]
|
||||
self.cost = stats["cost"]
|
||||
self.rect = pygame.Rect(x * CELL_SIZE, y * CELL_SIZE + TOP_MARGIN, CELL_SIZE, CELL_SIZE)
|
||||
self.shoot_timer = 0
|
||||
self.shoot_cooldown = 150 # Frames between shots
|
||||
self.eating_timer = 0
|
||||
self.eating_cooldown = 300 # Frames between chomps
|
||||
self.sun_timer = 0
|
||||
self.animation_state = "idle"
|
||||
self.damaged_state = 0
|
||||
|
||||
# Special abilities
|
||||
self.is_freezing = plant_type == PlantType.SNOW_PEA
|
||||
self.can_eat = plant_type == PlantType.CHOMPER
|
||||
self.is_rose = plant_type == PlantType.ROSE_SHOOTER
|
||||
|
||||
def update(self):
|
||||
if self.type in [PlantType.PEASHOOTER, PlantType.SNOW_PEA, PlantType.ROSE_SHOOTER]:
|
||||
self.shoot_timer += 1
|
||||
elif self.type == PlantType.SUNFLOWER:
|
||||
self.sun_timer += 1
|
||||
elif self.type == PlantType.CHOMPER and self.eating_timer > 0:
|
||||
self.eating_timer -= 1
|
||||
|
||||
# Update damage state
|
||||
health_percentage = self.health / self.max_health
|
||||
if health_percentage <= 0.3:
|
||||
self.damaged_state = 2
|
||||
elif health_percentage <= 0.6:
|
||||
self.damaged_state = 1
|
||||
|
||||
def can_shoot(self):
|
||||
if self.type == PlantType.PEASHOOTER:
|
||||
return self.shoot_timer >= 90
|
||||
elif self.type == PlantType.SNOW_PEA:
|
||||
return self.shoot_timer >= 90
|
||||
elif self.type == PlantType.ROSE_SHOOTER:
|
||||
return self.shoot_timer >= 100 # Slightly slower fire rate
|
||||
return False
|
||||
|
||||
def can_produce_sun(self):
|
||||
return self.type == PlantType.SUNFLOWER and self.sun_timer >= 360
|
||||
|
||||
def can_eat_zombie(self):
|
||||
return self.type == PlantType.CHOMPER and self.eating_timer <= 0
|
||||
|
||||
def start_eating(self):
|
||||
self.eating_timer = 300 # 5 seconds cooldown
|
||||
|
||||
def reset_timer(self):
|
||||
if self.type in [PlantType.PEASHOOTER, PlantType.SNOW_PEA, PlantType.ROSE_SHOOTER]:
|
||||
self.shoot_timer = 0
|
||||
elif self.type == PlantType.SUNFLOWER:
|
||||
self.sun_timer = 0
|
||||
|
||||
def draw(self, screen):
|
||||
# Draw shadow
|
||||
shadow_surface = pygame.Surface((CELL_SIZE, CELL_SIZE//4), pygame.SRCALPHA)
|
||||
pygame.draw.ellipse(shadow_surface, (0, 0, 0, 64), (0, 0, CELL_SIZE, CELL_SIZE//4))
|
||||
screen.blit(shadow_surface, (self.rect.x, self.rect.y + CELL_SIZE - CELL_SIZE//8))
|
||||
|
||||
# Draw plant using the corresponding drawing function
|
||||
if self.type in PLANT_DRAWINGS:
|
||||
PLANT_DRAWINGS[self.type](screen, self.rect.x, self.rect.y, CELL_SIZE)
|
||||
|
||||
# Draw health bar when damaged
|
||||
if self.health < self.max_health:
|
||||
health_width = max(0, (self.rect.width * self.health) // self.max_health)
|
||||
health_rect = pygame.Rect(self.rect.x, self.rect.y - 5, health_width, 3)
|
||||
pygame.draw.rect(screen, (255, 0, 0), health_rect)
|
||||
|
||||
class Zombie:
|
||||
def __init__(self, row, zombie_type):
|
||||
self.x = WINDOW_WIDTH / CELL_SIZE
|
||||
self.y = row
|
||||
self.type = zombie_type
|
||||
stats = ZOMBIE_STATS[zombie_type]
|
||||
self.health = stats["health"]
|
||||
self.max_health = stats["health"]
|
||||
self.speed = stats["speed"]
|
||||
self.damage = stats["damage"]
|
||||
self.rect = pygame.Rect(
|
||||
self.x * CELL_SIZE,
|
||||
self.y * CELL_SIZE + TOP_MARGIN,
|
||||
CELL_SIZE,
|
||||
CELL_SIZE
|
||||
)
|
||||
self.eating = False
|
||||
self.stun_timer = 0
|
||||
self.frozen_timer = 0
|
||||
self.frozen = False
|
||||
self.intoxicated_timer = 0
|
||||
self.intoxicated = False
|
||||
self.animation_state = "walking"
|
||||
|
||||
# Special abilities for newspaper zombie
|
||||
if self.type == ZombieType.NEWSPAPER:
|
||||
self.has_newspaper = True
|
||||
self.enraged = False
|
||||
else:
|
||||
self.has_newspaper = False
|
||||
self.enraged = False
|
||||
|
||||
# Special abilities for dancing zombie
|
||||
if self.type == ZombieType.DANCING:
|
||||
self.summon_timer = 300
|
||||
else:
|
||||
self.summon_timer = 0
|
||||
|
||||
def move(self):
|
||||
if not self.eating and self.stun_timer <= 0:
|
||||
actual_speed = self.speed
|
||||
if self.frozen:
|
||||
actual_speed *= 0.5
|
||||
if self.intoxicated:
|
||||
actual_speed *= 0.3
|
||||
if self.type == ZombieType.NEWSPAPER and self.enraged:
|
||||
actual_speed *= 1.5
|
||||
|
||||
self.x -= actual_speed / FPS
|
||||
self.rect.x = self.x * CELL_SIZE
|
||||
|
||||
if self.stun_timer > 0:
|
||||
self.stun_timer -= 1
|
||||
|
||||
if self.frozen:
|
||||
self.frozen_timer -= 1
|
||||
if self.frozen_timer <= 0:
|
||||
self.frozen = False
|
||||
|
||||
if self.intoxicated:
|
||||
self.intoxicated_timer -= 1
|
||||
if self.intoxicated_timer <= 0:
|
||||
self.intoxicated = False
|
||||
|
||||
def intoxicate(self):
|
||||
self.intoxicated = True
|
||||
self.intoxicated_timer = 300
|
||||
|
||||
def take_damage(self, damage):
|
||||
self.health -= damage
|
||||
if self.type == ZombieType.NEWSPAPER and self.has_newspaper and self.health <= self.max_health * 0.5:
|
||||
self.has_newspaper = False
|
||||
self.enraged = True
|
||||
self.speed *= 1.5
|
||||
|
||||
def freeze(self):
|
||||
self.frozen = True
|
||||
self.frozen_timer = 300
|
||||
|
||||
def draw(self, screen):
|
||||
# Draw shadow
|
||||
shadow_surface = pygame.Surface((CELL_SIZE, CELL_SIZE//3), pygame.SRCALPHA)
|
||||
pygame.draw.ellipse(shadow_surface, (0, 0, 0, 64), (0, 0, CELL_SIZE, CELL_SIZE//3))
|
||||
screen.blit(shadow_surface, (self.rect.x, self.rect.y + CELL_SIZE - CELL_SIZE//6))
|
||||
|
||||
# Draw zombie using the corresponding drawing function
|
||||
if self.type in ZOMBIE_DRAWINGS:
|
||||
ZOMBIE_DRAWINGS[self.type](screen, self.rect.x, self.rect.y, CELL_SIZE)
|
||||
|
||||
# Draw frozen effect
|
||||
if self.frozen:
|
||||
ice_surface = pygame.Surface((CELL_SIZE, CELL_SIZE), pygame.SRCALPHA)
|
||||
ice_surface.fill((150, 217, 255, 128))
|
||||
screen.blit(ice_surface, self.rect)
|
||||
|
||||
# Draw intoxicated effect
|
||||
if self.intoxicated:
|
||||
love_surface = pygame.Surface((CELL_SIZE, CELL_SIZE), pygame.SRCALPHA)
|
||||
time = pygame.time.get_ticks()
|
||||
for i in range(3):
|
||||
heart_x = self.rect.x + CELL_SIZE//2 + math.cos(time * 0.003 + i * 2) * 15
|
||||
heart_y = self.rect.y + CELL_SIZE//3 + math.sin(time * 0.003 + i * 2) * 10
|
||||
# Draw heart shape
|
||||
pygame.draw.circle(screen, (255, 192, 203, 200), (int(heart_x - 5), int(heart_y)), 5)
|
||||
pygame.draw.circle(screen, (255, 192, 203, 200), (int(heart_x + 5), int(heart_y)), 5)
|
||||
pygame.draw.polygon(screen, (255, 192, 203, 200), [
|
||||
(heart_x, heart_y + 8),
|
||||
(heart_x - 10, heart_y),
|
||||
(heart_x + 10, heart_y)
|
||||
])
|
||||
|
||||
# Health bar
|
||||
health_width = max(0, (self.rect.width * self.health) // self.max_health)
|
||||
health_rect = pygame.Rect(self.rect.x, self.rect.y - 10, health_width, 5)
|
||||
pygame.draw.rect(screen, (255, 0, 0), health_rect)
|
||||
|
||||
class Projectile:
|
||||
def __init__(self, x, y, damage=20, speed=5, freezing=False, is_rose=False):
|
||||
self.x = (x + 0.5) * CELL_SIZE
|
||||
self.y = y * CELL_SIZE + TOP_MARGIN + CELL_SIZE // 2
|
||||
self.damage = damage
|
||||
self.speed = speed
|
||||
self.freezing = freezing
|
||||
self.is_rose = is_rose
|
||||
self.active = True
|
||||
self.size = 10 # Set all projectiles to the same size
|
||||
self.rect = pygame.Rect(self.x - self.size//2, self.y - self.size//2, self.size, self.size)
|
||||
self.trail_positions = []
|
||||
self.trail_lifetime = 15 if is_rose else 10
|
||||
self.glow_offset = 0
|
||||
self.rotation = 0
|
||||
self.color = (255, 192, 203) if is_rose else ((0, 191, 255) if freezing else (0, 255, 0))
|
||||
self.alpha = 255
|
||||
|
||||
def move(self):
|
||||
# Store current position for trail
|
||||
self.trail_positions.append((self.rect.x, self.rect.y))
|
||||
if len(self.trail_positions) > self.trail_lifetime:
|
||||
self.trail_positions.pop(0)
|
||||
|
||||
self.rect.x += self.speed
|
||||
self.rotation += 15 # Rotate 15 degrees per frame
|
||||
|
||||
# Update glow effect
|
||||
self.glow_offset = abs(math.sin(pygame.time.get_ticks() * 0.01)) * 2
|
||||
|
||||
if self.rect.x > WINDOW_WIDTH:
|
||||
self.active = False
|
||||
|
||||
def draw(self, screen):
|
||||
# Draw trail with rose petals or regular trail
|
||||
for i, (x, y) in enumerate(self.trail_positions):
|
||||
alpha = int(255 * (i / len(self.trail_positions)) * 0.5)
|
||||
size = int(4 * (i / len(self.trail_positions))) # Same size for all trails
|
||||
|
||||
trail_surface = pygame.Surface((12, 12), pygame.SRCALPHA) # Same size for all trails
|
||||
if self.is_rose:
|
||||
# Draw rose petal trail
|
||||
petal_color = (255, 192, 203, alpha) # Pink with alpha
|
||||
# Draw multiple petals for a more detailed trail
|
||||
for angle in range(0, 360, 72):
|
||||
rad = math.radians(angle + self.rotation)
|
||||
petal_x = 6 + math.cos(rad) * size # Center at 6 (half of 12)
|
||||
petal_y = 6 + math.sin(rad) * size
|
||||
pygame.draw.circle(trail_surface, petal_color, (int(petal_x), int(petal_y)), size)
|
||||
else:
|
||||
color = (0, 191, 255, alpha) if self.freezing else (0, 255, 0, alpha)
|
||||
pygame.draw.circle(trail_surface, color, (6, 6), size)
|
||||
screen.blit(trail_surface, (x - 6, y - 6))
|
||||
|
||||
# Draw main projectile
|
||||
if self.is_rose:
|
||||
# Draw rose projectile at same size as others
|
||||
glow_surface = pygame.Surface((20, 20), pygame.SRCALPHA)
|
||||
glow_radius = 8 + self.glow_offset
|
||||
glow_color = (255, 192, 203, 64) # Pink with transparency
|
||||
pygame.draw.circle(glow_surface, glow_color, (10, 10), glow_radius)
|
||||
screen.blit(glow_surface, (self.rect.x - 10, self.rect.y - 10))
|
||||
|
||||
# Main projectile
|
||||
pygame.draw.circle(screen, (255, 192, 203), (self.rect.x, self.rect.y), 6) # Same size as others
|
||||
pygame.draw.circle(screen, (255, 105, 180), (self.rect.x, self.rect.y), 4) # Inner color
|
||||
|
||||
# Add highlight
|
||||
highlight_pos = (self.rect.x - 2, self.rect.y - 2)
|
||||
pygame.draw.circle(screen, (255, 255, 255, 180), highlight_pos, 2)
|
||||
else:
|
||||
# Draw regular projectile
|
||||
glow_color = (0, 191, 255, 64) if self.freezing else (0, 255, 0, 64)
|
||||
main_color = (0, 191, 255) if self.freezing else (0, 200, 0)
|
||||
inner_color = (173, 216, 230) if self.freezing else (150, 255, 150)
|
||||
|
||||
glow_surface = pygame.Surface((20, 20), pygame.SRCALPHA)
|
||||
glow_radius = 8 + self.glow_offset
|
||||
pygame.draw.circle(glow_surface, glow_color, (10, 10), glow_radius)
|
||||
screen.blit(glow_surface, (self.rect.x - 10, self.rect.y - 10))
|
||||
|
||||
pygame.draw.circle(screen, main_color, (self.rect.x, self.rect.y), 6)
|
||||
pygame.draw.circle(screen, inner_color, (self.rect.x, self.rect.y), 4)
|
||||
|
||||
highlight_pos = (self.rect.x - 2, self.rect.y - 2)
|
||||
pygame.draw.circle(screen, (255, 255, 255, 180), highlight_pos, 2)
|
||||
817
WareHouse/pvz_THUNLPDemo_2024/main.py
Normal file
@ -0,0 +1,817 @@
|
||||
import pygame
|
||||
import sys
|
||||
import random
|
||||
import math
|
||||
from constants import *
|
||||
from entities import Plant, Zombie, Projectile
|
||||
from sprites import PLANT_DRAWINGS, ZOMBIE_DRAWINGS
|
||||
|
||||
class Sun:
|
||||
def __init__(self, x, y, from_sky=True):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.initial_x = x
|
||||
self.initial_y = y
|
||||
self.value = 25
|
||||
self.rect = pygame.Rect(x, y, 40, 40)
|
||||
self.collected = False
|
||||
self.from_sky = from_sky
|
||||
self.fall_speed = 1.5
|
||||
self.lifetime = 300 if from_sky else 450
|
||||
self.hover_offset = 0
|
||||
self.hover_speed = 0.03
|
||||
self.hover_range = 5
|
||||
self.fade_start = 60
|
||||
self.alpha = 0
|
||||
self.fade_in = 255
|
||||
self.glow_offset = 0
|
||||
self.size = 40
|
||||
self.collect_speed = 5
|
||||
self.collecting = False
|
||||
|
||||
def move(self):
|
||||
if self.from_sky and self.y < self.initial_y + WINDOW_HEIGHT//3:
|
||||
self.y += self.fall_speed
|
||||
self.rect.y = self.y
|
||||
|
||||
self.hover_offset = math.sin(pygame.time.get_ticks() * self.hover_speed) * self.hover_range
|
||||
self.rect.y = self.y + self.hover_offset
|
||||
|
||||
self.glow_offset = abs(math.sin(pygame.time.get_ticks() * 0.002)) * 5
|
||||
|
||||
if self.fade_in > 0:
|
||||
self.alpha = min(255, self.alpha + 10)
|
||||
self.fade_in -= 10
|
||||
|
||||
self.lifetime -= 1
|
||||
if self.lifetime <= self.fade_start:
|
||||
self.alpha = max(0, int(255 * (self.lifetime / self.fade_start)))
|
||||
|
||||
def draw(self, screen):
|
||||
sun_surface = pygame.Surface((50, 50), pygame.SRCALPHA)
|
||||
glow_radius = 25 + self.glow_offset
|
||||
pygame.draw.circle(sun_surface, (255, 255, 100, int(self.alpha * 0.3)), (25, 25), glow_radius)
|
||||
pygame.draw.circle(sun_surface, (255, 255, 0, self.alpha), (25, 25), 20)
|
||||
pygame.draw.circle(sun_surface, (255, 255, 200, self.alpha), (25, 25), 15)
|
||||
pygame.draw.circle(sun_surface, (255, 255, 255, int(self.alpha * 0.7)), (25, 25), 8)
|
||||
|
||||
screen.blit(sun_surface, (self.rect.x - 5, self.rect.y - 5))
|
||||
|
||||
class Game:
|
||||
def __init__(self):
|
||||
pygame.init()
|
||||
pygame.mixer.init()
|
||||
# Set window mode with fixed resolution
|
||||
self.screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
|
||||
pygame.display.set_caption("植物大战僵尸 - ChatDev制作")
|
||||
self.clock = pygame.time.Clock()
|
||||
self.state = GameState.MENU
|
||||
# Set scale factors based on window size
|
||||
self.base_width = WINDOW_WIDTH
|
||||
self.base_height = WINDOW_HEIGHT
|
||||
self.scale_x = 1.0
|
||||
self.scale_y = 1.0
|
||||
|
||||
# Load and play background music
|
||||
self.load_music()
|
||||
# Add Chinese font
|
||||
try:
|
||||
self.font = pygame.font.Font("/System/Library/Fonts/PingFang.ttc", 36)
|
||||
self.large_font = pygame.font.Font("/System/Library/Fonts/PingFang.ttc", 74)
|
||||
self.small_font = pygame.font.Font("/System/Library/Fonts/PingFang.ttc", 24)
|
||||
except:
|
||||
try:
|
||||
self.font = pygame.font.Font("/System/Library/Fonts/STHeiti Light.ttc", 36)
|
||||
self.large_font = pygame.font.Font("/System/Library/Fonts/STHeiti Light.ttc", 74)
|
||||
self.small_font = pygame.font.Font("/System/Library/Fonts/STHeiti Light.ttc", 24)
|
||||
except:
|
||||
try:
|
||||
self.font = pygame.font.Font("/System/Library/Fonts/Arial Unicode.ttf", 36)
|
||||
self.large_font = pygame.font.Font("/System/Library/Fonts/Arial Unicode.ttf", 74)
|
||||
self.small_font = pygame.font.Font("/System/Library/Fonts/Arial Unicode.ttf", 24)
|
||||
except:
|
||||
print("Warning: Could not load Chinese font, falling back to default font")
|
||||
self.font = pygame.font.Font(None, 36)
|
||||
self.large_font = pygame.font.Font(None, 74)
|
||||
self.small_font = pygame.font.Font(None, 24)
|
||||
self.reset_game()
|
||||
|
||||
def load_music(self):
|
||||
try:
|
||||
pygame.mixer.music.load("assets/music/bgm.mp3")
|
||||
pygame.mixer.music.set_volume(0.5) # Set volume to 50%
|
||||
pygame.mixer.music.play(-1) # -1 means loop indefinitely
|
||||
except:
|
||||
print("Warning: Could not load background music")
|
||||
|
||||
def reset_game(self):
|
||||
self.plants = []
|
||||
self.zombies = []
|
||||
self.projectiles = []
|
||||
self.suns = []
|
||||
self.particles = []
|
||||
self.sun_points = 2025
|
||||
self.selected_plant = None
|
||||
self.spawn_timer = 0
|
||||
self.sun_spawn_timer = 0
|
||||
self.wave_number = 1
|
||||
self.wave_timer = 600
|
||||
self.score = 0
|
||||
self.game_over = False
|
||||
|
||||
def spawn_sun(self):
|
||||
if self.sun_spawn_timer <= 0:
|
||||
x = random.randint(100, WINDOW_WIDTH - 100)
|
||||
self.suns.append(Sun(x, -40))
|
||||
self.sun_spawn_timer = random.randint(300, 500)
|
||||
self.sun_spawn_timer -= 1
|
||||
|
||||
def spawn_zombie(self):
|
||||
if self.spawn_timer <= 0:
|
||||
# Ensure minimum number of zombies (5) are present
|
||||
if len(self.zombies) < 5 + self.wave_number:
|
||||
# Spawn multiple zombies at once if below minimum
|
||||
zombies_to_spawn = max(2, 5 + self.wave_number - len(self.zombies))
|
||||
for _ in range(zombies_to_spawn):
|
||||
row = random.randint(0, GRID_ROWS - 1)
|
||||
|
||||
# Zombie type selection based on wave number
|
||||
zombie_types = [
|
||||
ZombieType.NORMAL,
|
||||
ZombieType.CONE,
|
||||
ZombieType.BUCKET,
|
||||
ZombieType.NEWSPAPER,
|
||||
ZombieType.DANCING
|
||||
]
|
||||
|
||||
zombie_type = random.choice(zombie_types)
|
||||
self.zombies.append(Zombie(row, zombie_type))
|
||||
else:
|
||||
# Regular spawn for maintaining zombie presence
|
||||
row = random.randint(0, GRID_ROWS - 1)
|
||||
zombie_types = [
|
||||
ZombieType.NORMAL,
|
||||
ZombieType.CONE,
|
||||
ZombieType.BUCKET,
|
||||
ZombieType.NEWSPAPER,
|
||||
ZombieType.DANCING
|
||||
]
|
||||
|
||||
zombie_type = random.choice(zombie_types)
|
||||
self.zombies.append(Zombie(row, zombie_type))
|
||||
|
||||
# Adjust spawn timer based on wave - make it faster
|
||||
base_timer = max(100, 300 - (self.wave_number * 40)) # Reduced from 500 to 300
|
||||
variation = random.randint(-30, 30) # Add some randomness
|
||||
self.spawn_timer = base_timer + variation
|
||||
self.spawn_timer -= 1
|
||||
|
||||
def handle_resize(self, event):
|
||||
# Update screen size
|
||||
width, height = event.size
|
||||
self.screen = pygame.display.set_mode((width, height), pygame.RESIZABLE)
|
||||
# Calculate new scale factors
|
||||
self.scale_x = width / self.base_width
|
||||
self.scale_y = height / self.base_height
|
||||
# Update font sizes based on scale
|
||||
scale = min(self.scale_x, self.scale_y)
|
||||
try:
|
||||
self.font = pygame.font.Font("/System/Library/Fonts/PingFang.ttc", int(36 * scale))
|
||||
self.large_font = pygame.font.Font("/System/Library/Fonts/PingFang.ttc", int(74 * scale))
|
||||
self.small_font = pygame.font.Font("/System/Library/Fonts/PingFang.ttc", int(24 * scale))
|
||||
except:
|
||||
try:
|
||||
self.font = pygame.font.Font("/System/Library/Fonts/STHeiti Light.ttc", int(36 * scale))
|
||||
self.large_font = pygame.font.Font("/System/Library/Fonts/STHeiti Light.ttc", int(74 * scale))
|
||||
self.small_font = pygame.font.Font("/System/Library/Fonts/STHeiti Light.ttc", int(24 * scale))
|
||||
except:
|
||||
try:
|
||||
self.font = pygame.font.Font("/System/Library/Fonts/Arial Unicode.ttf", int(36 * scale))
|
||||
self.large_font = pygame.font.Font("/System/Library/Fonts/Arial Unicode.ttf", int(74 * scale))
|
||||
self.small_font = pygame.font.Font("/System/Library/Fonts/Arial Unicode.ttf", int(24 * scale))
|
||||
except:
|
||||
self.font = pygame.font.Font(None, int(36 * scale))
|
||||
self.large_font = pygame.font.Font(None, int(74 * scale))
|
||||
self.small_font = pygame.font.Font(None, int(24 * scale))
|
||||
|
||||
def get_scaled_rect(self, rect):
|
||||
# Helper function to scale rectangles
|
||||
return pygame.Rect(
|
||||
rect.x * self.scale_x,
|
||||
rect.y * self.scale_y,
|
||||
rect.width * self.scale_x,
|
||||
rect.height * self.scale_y
|
||||
)
|
||||
|
||||
def get_real_pos(self, pos):
|
||||
# Convert screen position to game logic position
|
||||
return (pos[0] / self.scale_x, pos[1] / self.scale_y)
|
||||
|
||||
def handle_click(self, pos):
|
||||
# Convert screen position to game logic position
|
||||
x, y = self.get_real_pos(pos)
|
||||
|
||||
# Check if clicking on a sun
|
||||
for sun in self.suns[:]:
|
||||
if sun.rect.collidepoint(x, y) and not sun.collected:
|
||||
self.sun_points += sun.value
|
||||
self.suns.remove(sun)
|
||||
continue
|
||||
|
||||
# Plant placement
|
||||
# Calculate grid position
|
||||
grid_x = int(x // CELL_SIZE)
|
||||
grid_y = int((y - TOP_MARGIN) // CELL_SIZE)
|
||||
|
||||
# Check if click is within the planting area
|
||||
if 0 <= grid_x < GRID_COLS and 0 <= grid_y < GRID_ROWS:
|
||||
# Check if there's already a plant there
|
||||
plant_exists = any(p.x == grid_x and p.y == grid_y for p in self.plants)
|
||||
|
||||
if not plant_exists and self.selected_plant:
|
||||
cost = PLANT_STATS[self.selected_plant]["cost"]
|
||||
if self.sun_points >= cost:
|
||||
self.plants.append(Plant(grid_x, grid_y, self.selected_plant))
|
||||
self.sun_points -= cost
|
||||
self.selected_plant = None
|
||||
|
||||
def update_plants(self):
|
||||
for plant in self.plants:
|
||||
plant.update()
|
||||
if plant.can_shoot():
|
||||
if plant.type == PlantType.SNOW_PEA:
|
||||
self.projectiles.append(Projectile(plant.x, plant.y, freezing=True))
|
||||
elif plant.type == PlantType.ROSE_SHOOTER:
|
||||
# Shoot in current lane and adjacent lanes
|
||||
lanes = [plant.y] # Current lane
|
||||
if plant.y > 0: # Add lane above if exists
|
||||
lanes.append(plant.y - 1)
|
||||
if plant.y < GRID_ROWS - 1: # Add lane below if exists
|
||||
lanes.append(plant.y + 1)
|
||||
for lane in lanes:
|
||||
# Create rose projectile with special properties
|
||||
proj = Projectile(plant.x, lane, damage=20, speed=6, is_rose=True)
|
||||
self.projectiles.append(proj)
|
||||
else:
|
||||
self.projectiles.append(Projectile(plant.x, plant.y))
|
||||
plant.reset_timer()
|
||||
elif plant.can_produce_sun():
|
||||
self.suns.append(Sun(plant.rect.x, plant.rect.y, from_sky=False))
|
||||
plant.reset_timer()
|
||||
elif plant.can_eat_zombie():
|
||||
# Check for zombies in range for Chomper
|
||||
for zombie in self.zombies[:]:
|
||||
if zombie.y == plant.y and abs(zombie.x - plant.x) <= 1:
|
||||
self.zombies.remove(zombie)
|
||||
plant.start_eating()
|
||||
self.score += 100
|
||||
break
|
||||
|
||||
def update_combat(self):
|
||||
# Update projectiles and check collisions
|
||||
for projectile in self.projectiles[:]:
|
||||
if not projectile.active:
|
||||
self.projectiles.remove(projectile)
|
||||
continue
|
||||
|
||||
projectile.move()
|
||||
for zombie in self.zombies[:]:
|
||||
if projectile.rect.colliderect(zombie.rect):
|
||||
# Create impact effect based on projectile type
|
||||
if projectile.is_rose:
|
||||
# Rose shooter effect (pink petals)
|
||||
color = (255, 192, 203) # Pink for rose
|
||||
for _ in range(12):
|
||||
angle = random.uniform(0, 2 * math.pi)
|
||||
speed = random.uniform(3, 6)
|
||||
size = random.uniform(4, 7)
|
||||
self.particles.append({
|
||||
'x': projectile.rect.x,
|
||||
'y': projectile.rect.y,
|
||||
'dx': math.cos(angle) * speed,
|
||||
'dy': math.sin(angle) * speed,
|
||||
'lifetime': 45,
|
||||
'color': (255, 192, 203),
|
||||
'size': size,
|
||||
'rotation': random.uniform(0, 360),
|
||||
'is_petal': True,
|
||||
'shape': 'petal'
|
||||
})
|
||||
# Add sparkle particle
|
||||
self.particles.append({
|
||||
'x': projectile.rect.x,
|
||||
'y': projectile.rect.y,
|
||||
'dx': math.cos(angle) * (speed * 0.7),
|
||||
'dy': math.sin(angle) * (speed * 0.7),
|
||||
'lifetime': 30,
|
||||
'color': (255, 255, 255),
|
||||
'size': size * 0.5,
|
||||
'is_petal': False
|
||||
})
|
||||
zombie.intoxicate()
|
||||
elif projectile.freezing:
|
||||
# Snow pea effect (ice crystals)
|
||||
color = (0, 191, 255) # Ice blue
|
||||
for _ in range(12):
|
||||
angle = random.uniform(0, 2 * math.pi)
|
||||
speed = random.uniform(3, 6)
|
||||
size = random.uniform(4, 7)
|
||||
self.particles.append({
|
||||
'x': projectile.rect.x,
|
||||
'y': projectile.rect.y,
|
||||
'dx': math.cos(angle) * speed,
|
||||
'dy': math.sin(angle) * speed,
|
||||
'lifetime': 40,
|
||||
'color': (0, 191, 255),
|
||||
'size': size,
|
||||
'rotation': random.uniform(0, 360),
|
||||
'is_petal': True,
|
||||
'shape': 'snowflake'
|
||||
})
|
||||
# Add sparkle particle
|
||||
self.particles.append({
|
||||
'x': projectile.rect.x,
|
||||
'y': projectile.rect.y,
|
||||
'dx': math.cos(angle) * (speed * 0.7),
|
||||
'dy': math.sin(angle) * (speed * 0.7),
|
||||
'lifetime': 25,
|
||||
'color': (255, 255, 255),
|
||||
'size': size * 0.4,
|
||||
'is_petal': False
|
||||
})
|
||||
zombie.freeze()
|
||||
else:
|
||||
# Regular peashooter effect (leaves and splashes)
|
||||
color = (0, 255, 0) # Green
|
||||
for _ in range(12):
|
||||
angle = random.uniform(0, 2 * math.pi)
|
||||
speed = random.uniform(3, 6)
|
||||
size = random.uniform(4, 7)
|
||||
self.particles.append({
|
||||
'x': projectile.rect.x,
|
||||
'y': projectile.rect.y,
|
||||
'dx': math.cos(angle) * speed,
|
||||
'dy': math.sin(angle) * speed,
|
||||
'lifetime': 35,
|
||||
'color': (0, 200, 0),
|
||||
'size': size,
|
||||
'rotation': random.uniform(0, 360),
|
||||
'is_petal': True,
|
||||
'shape': 'leaf'
|
||||
})
|
||||
# Add splash particle
|
||||
self.particles.append({
|
||||
'x': projectile.rect.x,
|
||||
'y': projectile.rect.y,
|
||||
'dx': math.cos(angle) * (speed * 0.8),
|
||||
'dy': math.sin(angle) * (speed * 0.8),
|
||||
'lifetime': 20,
|
||||
'color': (150, 255, 150),
|
||||
'size': size * 0.6,
|
||||
'is_petal': False
|
||||
})
|
||||
|
||||
zombie.take_damage(projectile.damage)
|
||||
if projectile.freezing:
|
||||
zombie.freeze()
|
||||
zombie.stun_timer = 2
|
||||
if zombie.health <= 0:
|
||||
# Add death particles
|
||||
for _ in range(12):
|
||||
angle = random.uniform(0, 2 * math.pi)
|
||||
speed = random.uniform(3, 6)
|
||||
self.particles.append({
|
||||
'x': zombie.rect.x + CELL_SIZE//2,
|
||||
'y': zombie.rect.y + CELL_SIZE//2,
|
||||
'dx': math.cos(angle) * speed,
|
||||
'dy': math.sin(angle) * speed,
|
||||
'lifetime': 30,
|
||||
'color': (139, 69, 19), # Brown for zombie parts
|
||||
'size': random.uniform(3, 6)
|
||||
})
|
||||
self.zombies.remove(zombie)
|
||||
self.score += 100
|
||||
if projectile in self.projectiles:
|
||||
self.projectiles.remove(projectile)
|
||||
break
|
||||
|
||||
# Update particles
|
||||
for particle in self.particles[:]:
|
||||
particle['x'] += particle['dx']
|
||||
particle['y'] += particle['dy']
|
||||
particle['lifetime'] -= 1
|
||||
if particle['lifetime'] <= 0:
|
||||
self.particles.remove(particle)
|
||||
|
||||
# Check zombie-plant interactions
|
||||
for zombie in self.zombies:
|
||||
for plant in self.plants[:]:
|
||||
if zombie.rect.colliderect(plant.rect):
|
||||
zombie.eating = True
|
||||
plant.health -= zombie.damage
|
||||
if plant.health <= 0:
|
||||
# Add plant death particles
|
||||
for _ in range(8):
|
||||
angle = random.uniform(0, 2 * math.pi)
|
||||
speed = random.uniform(2, 4)
|
||||
self.particles.append({
|
||||
'x': plant.rect.x + CELL_SIZE//2,
|
||||
'y': plant.rect.y + CELL_SIZE//2,
|
||||
'dx': math.cos(angle) * speed,
|
||||
'dy': math.sin(angle) * speed,
|
||||
'lifetime': 25,
|
||||
'color': (0, 100, 0), # Dark green for plant parts
|
||||
'size': random.uniform(2, 5)
|
||||
})
|
||||
self.plants.remove(plant)
|
||||
zombie.eating = False
|
||||
break
|
||||
else:
|
||||
zombie.eating = False
|
||||
|
||||
def draw_lawn(self):
|
||||
# Create a surface for the lawn at base size
|
||||
lawn_surface = pygame.Surface((self.base_width, self.base_height))
|
||||
lawn_surface.fill(LAWN_GREEN)
|
||||
|
||||
# Draw grid with better visuals
|
||||
for row in range(GRID_ROWS):
|
||||
for col in range(GRID_COLS):
|
||||
rect = pygame.Rect(
|
||||
col * CELL_SIZE,
|
||||
row * CELL_SIZE + TOP_MARGIN,
|
||||
CELL_SIZE,
|
||||
CELL_SIZE
|
||||
)
|
||||
if (row + col) % 2 == 0:
|
||||
pygame.draw.rect(lawn_surface, (115, 235, 0), rect)
|
||||
pygame.draw.rect(lawn_surface, (100, 200, 0), rect, 1)
|
||||
|
||||
# Scale and blit to screen
|
||||
scaled_surface = pygame.transform.scale(lawn_surface, self.screen.get_size())
|
||||
self.screen.blit(scaled_surface, (0, 0))
|
||||
|
||||
def draw_plant_menu(self):
|
||||
menu_height = 100 * self.scale_y
|
||||
menu_surface = pygame.Surface((self.screen.get_width(), menu_height), pygame.SRCALPHA)
|
||||
pygame.draw.rect(menu_surface, (139, 69, 19, 200), (0, 0, self.screen.get_width(), menu_height))
|
||||
|
||||
# Get mouse position for hover effect
|
||||
mouse_x, mouse_y = pygame.mouse.get_pos()
|
||||
menu_y = mouse_y - (self.screen.get_height() - menu_height)
|
||||
|
||||
# Plant cards
|
||||
cards = [
|
||||
(PlantType.SUNFLOWER, YELLOW, 50),
|
||||
(PlantType.PEASHOOTER, GREEN, 100),
|
||||
(PlantType.ROSE_SHOOTER, (255, 192, 203), 125), # Pink color for rose
|
||||
(PlantType.CHOMPER, (148, 0, 211), 150),
|
||||
(PlantType.SNOW_PEA, (0, 191, 255), 175)
|
||||
]
|
||||
|
||||
card_width = 70 * self.scale_x
|
||||
card_height = 80 * self.scale_y
|
||||
card_spacing = 90 * self.scale_x
|
||||
|
||||
for i, (plant_type, color, cost) in enumerate(cards):
|
||||
card_x = 10 * self.scale_x + i * card_spacing
|
||||
card_rect = pygame.Rect(card_x, 10 * self.scale_y, card_width, card_height)
|
||||
|
||||
# Check if card is hovered or selected
|
||||
is_hovered = (0 <= menu_y <= card_height + 20 * self.scale_y and
|
||||
card_x <= mouse_x <= card_x + card_width)
|
||||
is_selected = self.selected_plant == plant_type
|
||||
|
||||
# Draw card background with hover/selected effect
|
||||
if is_selected:
|
||||
# Glowing effect for selected card
|
||||
glow_surface = pygame.Surface((card_width + 4, card_height + 4), pygame.SRCALPHA)
|
||||
pygame.draw.rect(glow_surface, (*PLANT_STATS[plant_type]["color"], 128),
|
||||
(0, 0, card_width + 4, card_height + 4))
|
||||
menu_surface.blit(glow_surface, (card_rect.x - 2, card_rect.y - 2))
|
||||
pygame.draw.rect(menu_surface, WHITE,
|
||||
(card_rect.x - 2, card_rect.y - 2, card_width + 4, card_height + 4),
|
||||
max(1, int(2 * self.scale_x)))
|
||||
elif is_hovered:
|
||||
# Hover effect
|
||||
pygame.draw.rect(menu_surface, (255, 255, 255, 30), card_rect)
|
||||
|
||||
pygame.draw.rect(menu_surface, color, card_rect)
|
||||
|
||||
# Draw plant image on card
|
||||
if plant_type in PLANT_DRAWINGS:
|
||||
# Create a smaller surface for the plant
|
||||
plant_surface = pygame.Surface((CELL_SIZE, CELL_SIZE), pygame.SRCALPHA)
|
||||
PLANT_DRAWINGS[plant_type](plant_surface, 0, 0, CELL_SIZE)
|
||||
# Scale it down to fit the card
|
||||
scaled_size = (int(50 * self.scale_x), int(50 * self.scale_y))
|
||||
scaled_surface = pygame.transform.scale(plant_surface, scaled_size)
|
||||
menu_surface.blit(scaled_surface,
|
||||
(card_rect.x + 10 * self.scale_x,
|
||||
card_rect.y + 5 * self.scale_y))
|
||||
|
||||
# Cost indicator with sun icon
|
||||
sun_size = 10 * min(self.scale_x, self.scale_y)
|
||||
pygame.draw.circle(menu_surface, YELLOW,
|
||||
(card_rect.x + sun_size, card_rect.bottom - sun_size),
|
||||
sun_size)
|
||||
cost_text = self.small_font.render(str(cost), True, BLACK)
|
||||
menu_surface.blit(cost_text,
|
||||
(card_rect.x + sun_size * 2,
|
||||
card_rect.bottom - sun_size * 1.5))
|
||||
|
||||
# Gray out if can't afford
|
||||
if self.sun_points < cost:
|
||||
gray_surface = pygame.Surface((card_width, card_height), pygame.SRCALPHA)
|
||||
pygame.draw.rect(gray_surface, (128, 128, 128, 180),
|
||||
(0, 0, card_width, card_height))
|
||||
menu_surface.blit(gray_surface, card_rect)
|
||||
|
||||
self.screen.blit(menu_surface, (0, self.screen.get_height() - menu_height))
|
||||
|
||||
def draw_hud(self):
|
||||
# Sun points
|
||||
sun_size = 30 * min(self.scale_x, self.scale_y)
|
||||
sun_icon = pygame.Surface((sun_size, sun_size), pygame.SRCALPHA)
|
||||
pygame.draw.circle(sun_icon, YELLOW, (sun_size/2, sun_size/2), sun_size/2)
|
||||
self.screen.blit(sun_icon, (10 * self.scale_x, 10 * self.scale_y))
|
||||
|
||||
sun_text = self.font.render(str(self.sun_points), True, BLACK)
|
||||
self.screen.blit(sun_text, (45 * self.scale_x, 15 * self.scale_y))
|
||||
|
||||
# Wave number
|
||||
wave_text = self.font.render(f"第 {self.wave_number} 波僵尸", True, BLACK)
|
||||
self.screen.blit(wave_text,
|
||||
(self.screen.get_width() - 200 * self.scale_x,
|
||||
15 * self.scale_y))
|
||||
|
||||
# Score
|
||||
score_text = self.font.render(f"得分: {self.score}", True, BLACK)
|
||||
self.screen.blit(score_text,
|
||||
(self.screen.get_width()//2 - score_text.get_width()//2,
|
||||
15 * self.scale_y))
|
||||
|
||||
def draw_watermark(self):
|
||||
watermark = self.small_font.render("ChatDev制作", True, (0, 0, 0, 128))
|
||||
watermark.set_alpha(128) # Make it semi-transparent
|
||||
self.screen.blit(watermark,
|
||||
(self.screen.get_width() - watermark.get_width() - 10,
|
||||
self.screen.get_height() - watermark.get_height() - 10))
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
if self.state == GameState.MENU:
|
||||
self.run_menu()
|
||||
elif self.state == GameState.PLAYING:
|
||||
self.run_game()
|
||||
elif self.state == GameState.GAME_OVER:
|
||||
self.run_game_over()
|
||||
|
||||
def run_menu(self):
|
||||
while self.state == GameState.MENU:
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
elif event.type == pygame.MOUSEBUTTONDOWN:
|
||||
if event.button == 1: # Left click
|
||||
# Start game button area
|
||||
button_rect = pygame.Rect(
|
||||
self.screen.get_width()//2 - 100 * self.scale_x,
|
||||
self.screen.get_height()//2,
|
||||
200 * self.scale_x,
|
||||
50 * self.scale_y
|
||||
)
|
||||
if button_rect.collidepoint(event.pos):
|
||||
self.state = GameState.PLAYING
|
||||
self.reset_game()
|
||||
elif event.type == pygame.VIDEORESIZE:
|
||||
self.handle_resize(event)
|
||||
|
||||
# Draw menu
|
||||
self.screen.fill(LAWN_GREEN)
|
||||
|
||||
# Draw title
|
||||
title = self.large_font.render("植物大战僵尸", True, BLACK)
|
||||
self.screen.blit(title,
|
||||
(self.screen.get_width()//2 - title.get_width()//2,
|
||||
self.screen.get_height()//4))
|
||||
|
||||
# Draw start button
|
||||
button_rect = pygame.Rect(
|
||||
self.screen.get_width()//2 - 100 * self.scale_x,
|
||||
self.screen.get_height()//2,
|
||||
200 * self.scale_x,
|
||||
50 * self.scale_y
|
||||
)
|
||||
pygame.draw.rect(self.screen, GREEN, button_rect)
|
||||
pygame.draw.rect(self.screen, BLACK, button_rect, 2)
|
||||
|
||||
start_text = self.font.render("开始游戏", True, BLACK)
|
||||
self.screen.blit(start_text,
|
||||
(self.screen.get_width()//2 - start_text.get_width()//2,
|
||||
self.screen.get_height()//2 + 5 * self.scale_y))
|
||||
|
||||
self.draw_watermark()
|
||||
|
||||
pygame.display.flip()
|
||||
self.clock.tick(FPS)
|
||||
|
||||
def run_game(self):
|
||||
while self.state == GameState.PLAYING and not self.game_over:
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
elif event.type == pygame.VIDEORESIZE:
|
||||
self.handle_resize(event)
|
||||
elif event.type == pygame.MOUSEBUTTONDOWN:
|
||||
mouse_pos = pygame.mouse.get_pos()
|
||||
real_pos = self.get_real_pos(mouse_pos)
|
||||
if self.base_height - 100 <= real_pos[1] <= self.base_height:
|
||||
menu_y = real_pos[1] - (self.base_height - 100)
|
||||
if 10 <= menu_y <= 90:
|
||||
card_x = int((real_pos[0] - 10) // 90) # Convert to integer
|
||||
if 0 <= card_x <= 4:
|
||||
plant_types = [
|
||||
PlantType.SUNFLOWER,
|
||||
PlantType.PEASHOOTER,
|
||||
PlantType.ROSE_SHOOTER,
|
||||
PlantType.CHOMPER,
|
||||
PlantType.SNOW_PEA
|
||||
]
|
||||
if card_x < len(plant_types):
|
||||
plant_type = plant_types[card_x]
|
||||
if self.sun_points >= PLANT_STATS[plant_type]["cost"]:
|
||||
self.selected_plant = plant_type
|
||||
else:
|
||||
self.handle_click(mouse_pos)
|
||||
|
||||
# Update game state
|
||||
self.spawn_zombie()
|
||||
self.spawn_sun()
|
||||
self.update_plants()
|
||||
self.update_combat()
|
||||
|
||||
# Wave management
|
||||
self.wave_timer -= 1
|
||||
if self.wave_timer <= 0:
|
||||
self.wave_number += 1
|
||||
self.wave_timer = 600
|
||||
|
||||
# Move entities
|
||||
for zombie in self.zombies:
|
||||
zombie.move()
|
||||
if zombie.x <= 0:
|
||||
self.game_over = True
|
||||
self.state = GameState.GAME_OVER
|
||||
|
||||
for sun in self.suns[:]:
|
||||
sun.move()
|
||||
if sun.lifetime <= 0:
|
||||
self.suns.remove(sun)
|
||||
|
||||
# Draw everything
|
||||
self.draw_lawn()
|
||||
|
||||
# Create a game surface at base size and draw everything on it
|
||||
game_surface = pygame.Surface((self.base_width, self.base_height), pygame.SRCALPHA)
|
||||
|
||||
for plant in self.plants:
|
||||
plant.draw(game_surface)
|
||||
|
||||
for zombie in self.zombies:
|
||||
zombie.draw(game_surface)
|
||||
|
||||
for projectile in self.projectiles:
|
||||
projectile.draw(game_surface)
|
||||
|
||||
for sun in self.suns:
|
||||
sun.draw(game_surface)
|
||||
|
||||
# Draw particles
|
||||
for particle in self.particles:
|
||||
if particle.get('is_petal', False):
|
||||
# Draw shaped particles based on type
|
||||
shape = particle.get('shape', 'petal')
|
||||
particle_surface = pygame.Surface((particle['size'] * 2, particle['size'] * 2), pygame.SRCALPHA)
|
||||
center = (particle['size'], particle['size'])
|
||||
|
||||
if shape == 'petal':
|
||||
# Draw rose petal shape
|
||||
for angle in range(0, 360, 72):
|
||||
rad = math.radians(angle + particle['rotation'])
|
||||
petal_x = center[0] + math.cos(rad) * particle['size']
|
||||
petal_y = center[1] + math.sin(rad) * particle['size']
|
||||
pygame.draw.circle(particle_surface, particle['color'],
|
||||
(int(petal_x), int(petal_y)),
|
||||
int(particle['size'] * 0.6))
|
||||
|
||||
elif shape == 'snowflake':
|
||||
# Draw snowflake shape
|
||||
for angle in range(0, 360, 45):
|
||||
rad = math.radians(angle + particle['rotation'])
|
||||
# Draw main line
|
||||
end_x = center[0] + math.cos(rad) * particle['size']
|
||||
end_y = center[1] + math.sin(rad) * particle['size']
|
||||
pygame.draw.line(particle_surface, particle['color'],
|
||||
center, (int(end_x), int(end_y)), 2)
|
||||
# Draw side branches
|
||||
branch_length = particle['size'] * 0.5
|
||||
mid_x = center[0] + math.cos(rad) * particle['size'] * 0.6
|
||||
mid_y = center[1] + math.sin(rad) * particle['size'] * 0.6
|
||||
side_angle1 = rad + math.pi / 4
|
||||
side_angle2 = rad - math.pi / 4
|
||||
pygame.draw.line(particle_surface, particle['color'],
|
||||
(int(mid_x), int(mid_y)),
|
||||
(int(mid_x + math.cos(side_angle1) * branch_length),
|
||||
int(mid_y + math.sin(side_angle1) * branch_length)), 2)
|
||||
pygame.draw.line(particle_surface, particle['color'],
|
||||
(int(mid_x), int(mid_y)),
|
||||
(int(mid_x + math.cos(side_angle2) * branch_length),
|
||||
int(mid_y + math.sin(side_angle2) * branch_length)), 2)
|
||||
|
||||
elif shape == 'leaf':
|
||||
# Draw leaf shape
|
||||
points = []
|
||||
leaf_length = particle['size'] * 1.5
|
||||
leaf_width = particle['size'] * 0.8
|
||||
rad = math.radians(particle['rotation'])
|
||||
|
||||
# Create leaf shape points
|
||||
for t in range(0, 360, 10):
|
||||
t_rad = math.radians(t)
|
||||
x = center[0] + math.cos(rad) * leaf_length * math.cos(t_rad) - \
|
||||
math.sin(rad) * leaf_width * math.sin(t_rad)
|
||||
y = center[1] + math.sin(rad) * leaf_length * math.cos(t_rad) + \
|
||||
math.cos(rad) * leaf_width * math.sin(t_rad)
|
||||
points.append((int(x), int(y)))
|
||||
|
||||
if len(points) > 2:
|
||||
pygame.draw.polygon(particle_surface, particle['color'], points)
|
||||
# Draw leaf vein
|
||||
vein_start = center
|
||||
vein_end = (int(center[0] + math.cos(rad) * leaf_length),
|
||||
int(center[1] + math.sin(rad) * leaf_length))
|
||||
pygame.draw.line(particle_surface, (0, 150, 0),
|
||||
vein_start, vein_end, 1)
|
||||
|
||||
# Add fade out effect
|
||||
alpha = int(255 * (particle['lifetime'] / 45))
|
||||
particle_surface.set_alpha(alpha)
|
||||
game_surface.blit(particle_surface,
|
||||
(particle['x'] - particle['size'],
|
||||
particle['y'] - particle['size']))
|
||||
else:
|
||||
# Draw regular circular particles
|
||||
alpha = int(255 * (particle['lifetime'] / 30))
|
||||
color = (*particle['color'][:3], alpha)
|
||||
pygame.draw.circle(game_surface, color,
|
||||
(int(particle['x']), int(particle['y'])),
|
||||
int(particle['size']))
|
||||
|
||||
# Scale and blit the game surface
|
||||
scaled_surface = pygame.transform.scale(game_surface, self.screen.get_size())
|
||||
self.screen.blit(scaled_surface, (0, 0))
|
||||
|
||||
self.draw_plant_menu()
|
||||
self.draw_hud()
|
||||
self.draw_watermark()
|
||||
|
||||
pygame.display.flip()
|
||||
self.clock.tick(FPS)
|
||||
|
||||
def run_game_over(self):
|
||||
while self.state == GameState.GAME_OVER:
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
elif event.type == pygame.MOUSEBUTTONDOWN:
|
||||
if event.button == 1: # Left click
|
||||
self.state = GameState.MENU
|
||||
elif event.type == pygame.VIDEORESIZE:
|
||||
self.handle_resize(event)
|
||||
|
||||
# Draw game over screen
|
||||
self.screen.fill((0, 0, 0)) # Black background
|
||||
|
||||
# Draw game over text
|
||||
game_over_text = self.large_font.render("游戏结束", True, RED)
|
||||
score_text = self.font.render(f"最终得分: {self.score}", True, WHITE)
|
||||
|
||||
self.screen.blit(game_over_text,
|
||||
(self.screen.get_width()//2 - game_over_text.get_width()//2,
|
||||
self.screen.get_height()//3))
|
||||
self.screen.blit(score_text,
|
||||
(self.screen.get_width()//2 - score_text.get_width()//2,
|
||||
self.screen.get_height()//2))
|
||||
|
||||
self.draw_watermark()
|
||||
|
||||
pygame.display.flip()
|
||||
self.clock.tick(FPS)
|
||||
|
||||
if __name__ == "__main__":
|
||||
game = Game()
|
||||
game.run()
|
||||
1
WareHouse/pvz_THUNLPDemo_2024/requirements.txt
Normal file
@ -0,0 +1 @@
|
||||
pygame==2.5.2
|
||||
561
WareHouse/pvz_THUNLPDemo_2024/sprites.py
Normal file
@ -0,0 +1,561 @@
|
||||
import pygame
|
||||
import math
|
||||
from constants import *
|
||||
|
||||
def draw_sunflower(surface, x, y, size):
|
||||
# Get animation offset based on time
|
||||
time = pygame.time.get_ticks()
|
||||
sway = math.sin(time * 0.003) * 3
|
||||
petal_spin = time * 0.002
|
||||
|
||||
# Draw stem with swaying animation
|
||||
stem_points = [
|
||||
(x + size//2 + sway, y + size*3//4),
|
||||
(x + size//2, y + size*7//8),
|
||||
(x + size//2, y + size)
|
||||
]
|
||||
pygame.draw.lines(surface, GREEN, False, stem_points, 3)
|
||||
|
||||
# Draw leaves
|
||||
leaf_color = (34, 139, 34) # Forest green
|
||||
leaf_points = [
|
||||
(x + size//2, y + size*3//4),
|
||||
(x + size//3, y + size*7//8),
|
||||
(x + size//2, y + size*13//16)
|
||||
]
|
||||
pygame.draw.polygon(surface, leaf_color, leaf_points)
|
||||
leaf_points = [
|
||||
(x + size//2, y + size*3//4),
|
||||
(x + size*2//3, y + size*7//8),
|
||||
(x + size//2, y + size*13//16)
|
||||
]
|
||||
pygame.draw.polygon(surface, leaf_color, leaf_points)
|
||||
|
||||
# Draw center with gradient
|
||||
center_x, center_y = x + size//2 + sway, y + size//2
|
||||
pygame.draw.circle(surface, (160, 82, 45), (center_x, center_y), size//4) # Dark brown
|
||||
pygame.draw.circle(surface, (139, 69, 19), (center_x, center_y), size//5) # Medium brown
|
||||
pygame.draw.circle(surface, (101, 67, 33), (center_x, center_y), size//6) # Light brown
|
||||
|
||||
# Draw petals with rotation animation
|
||||
petal_colors = [(255, 218, 0), (255, 200, 0), (255, 182, 0)] # Different yellow shades
|
||||
for i, angle in enumerate(range(0, 360, 45)):
|
||||
rad = math.radians(angle + petal_spin)
|
||||
petal_x = center_x + math.cos(rad) * size//3
|
||||
petal_y = center_y + math.sin(rad) * size//3
|
||||
# Draw each petal with multiple layers for depth
|
||||
pygame.draw.circle(surface, petal_colors[i % 3], (int(petal_x), int(petal_y)), size//6)
|
||||
pygame.draw.circle(surface, petal_colors[(i + 1) % 3], (int(petal_x), int(petal_y)), size//8)
|
||||
|
||||
def draw_peashooter(surface, x, y, size):
|
||||
# Get animation offset
|
||||
time = pygame.time.get_ticks()
|
||||
sway = math.sin(time * 0.003) * 3
|
||||
|
||||
# Draw stem with swaying animation
|
||||
stem_points = [
|
||||
(x + size//2 + sway, y + size*2//3),
|
||||
(x + size//2, y + size*7//8),
|
||||
(x + size//2, y + size)
|
||||
]
|
||||
pygame.draw.lines(surface, GREEN, False, stem_points, 3)
|
||||
|
||||
# Draw leaves
|
||||
leaf_color = (34, 139, 34)
|
||||
leaf_points = [
|
||||
(x + size//2, y + size*3//4),
|
||||
(x + size//3, y + size*7//8),
|
||||
(x + size//2, y + size*13//16)
|
||||
]
|
||||
pygame.draw.polygon(surface, leaf_color, leaf_points)
|
||||
|
||||
# Draw head with gradient
|
||||
head_x = x + size//2 + sway
|
||||
head_color = (0, 200, 0)
|
||||
pygame.draw.ellipse(surface, head_color, (head_x - size//4, y + size//4, size//2, size//2))
|
||||
pygame.draw.ellipse(surface, (0, 180, 0), (head_x - size//5, y + size//3, size//2.5, size//2.5))
|
||||
|
||||
# Draw shooter with highlight
|
||||
shooter_x = head_x + size//4
|
||||
shooter_color = (0, 100, 0)
|
||||
pygame.draw.circle(surface, shooter_color, (int(shooter_x), y + size//2), size//7)
|
||||
pygame.draw.circle(surface, (0, 150, 0), (int(shooter_x), y + size//2), size//10)
|
||||
|
||||
# Draw eyes
|
||||
eye_color = (0, 0, 0)
|
||||
eye_x = head_x - size//8
|
||||
pygame.draw.ellipse(surface, eye_color, (eye_x, y + size//3, size//10, size//8))
|
||||
pygame.draw.ellipse(surface, eye_color, (eye_x + size//6, y + size//3, size//10, size//8))
|
||||
|
||||
def draw_wallnut(surface, x, y, size):
|
||||
# Get animation offset
|
||||
time = pygame.time.get_ticks()
|
||||
wobble = math.sin(time * 0.004) * 2
|
||||
|
||||
# Draw main body with gradient and texture
|
||||
nut_colors = [(139, 69, 19), (160, 82, 45), (205, 133, 63)]
|
||||
for i, color in enumerate(nut_colors):
|
||||
offset = i * 4
|
||||
pygame.draw.ellipse(surface, color,
|
||||
(x + size//6 + offset + wobble,
|
||||
y + size//6 + offset,
|
||||
size*2//3 - offset*2,
|
||||
size*2//3 - offset*2))
|
||||
|
||||
# Draw crack details
|
||||
crack_color = (101, 67, 33)
|
||||
crack_points = [
|
||||
(x + size//2, y + size//4),
|
||||
(x + size*2//3, y + size//3),
|
||||
(x + size//2, y + size//2)
|
||||
]
|
||||
pygame.draw.lines(surface, crack_color, False, crack_points, 2)
|
||||
|
||||
# Draw face with expression
|
||||
eye_color = BLACK
|
||||
blink = (time % 3000) < 200 # Blink every 3 seconds
|
||||
if not blink:
|
||||
# Draw eyes
|
||||
pygame.draw.ellipse(surface, eye_color, (x + size//3, y + size//3, size//6, size//6))
|
||||
pygame.draw.ellipse(surface, eye_color, (x + size//2, y + size//3, size//6, size//6))
|
||||
# Draw white highlights in eyes
|
||||
pygame.draw.circle(surface, WHITE, (x + size//3 + size//12, y + size//3 + size//12), size//20)
|
||||
pygame.draw.circle(surface, WHITE, (x + size//2 + size//12, y + size//3 + size//12), size//20)
|
||||
else:
|
||||
# Draw closed eyes
|
||||
pygame.draw.line(surface, eye_color, (x + size//3, y + size//3), (x + size//3 + size//6, y + size//3), 2)
|
||||
pygame.draw.line(surface, eye_color, (x + size//2, y + size//3), (x + size//2 + size//6, y + size//3), 2)
|
||||
|
||||
# Draw smile that changes with wobble
|
||||
smile_rect = pygame.Rect(x + size//3 + wobble, y + size//2, size//3, size//6)
|
||||
pygame.draw.arc(surface, eye_color, smile_rect, 0, math.pi, 2)
|
||||
|
||||
def draw_chomper(surface, x, y, size):
|
||||
# Get animation offset
|
||||
time = pygame.time.get_ticks()
|
||||
chomp = abs(math.sin(time * 0.004)) * size//4
|
||||
sway = math.sin(time * 0.003) * 3
|
||||
|
||||
# Draw stem with swaying animation
|
||||
stem_points = [
|
||||
(x + size//2 + sway, y + size*2//3),
|
||||
(x + size//2, y + size*7//8),
|
||||
(x + size//2, y + size)
|
||||
]
|
||||
pygame.draw.lines(surface, GREEN, False, stem_points, 4)
|
||||
|
||||
# Draw head
|
||||
head_color = (148, 0, 211) # Purple
|
||||
head_x = x + size//2 + sway
|
||||
|
||||
# Draw back of mouth
|
||||
pygame.draw.ellipse(surface, (101, 0, 148),
|
||||
(head_x - size//3, y + size//4, size*2//3, size//2))
|
||||
|
||||
# Draw tongue
|
||||
tongue_color = (255, 105, 180)
|
||||
tongue_points = [
|
||||
(head_x, y + size//2),
|
||||
(head_x - size//4, y + size//2 + size//4),
|
||||
(head_x + size//4, y + size//2 + size//4)
|
||||
]
|
||||
pygame.draw.polygon(surface, tongue_color, tongue_points)
|
||||
|
||||
# Draw mouth (upper and lower jaw)
|
||||
jaw_points_upper = [
|
||||
(head_x - size//3, y + size//3),
|
||||
(head_x + size//3, y + size//3),
|
||||
(head_x + size//2, y + size//2),
|
||||
(head_x - size//2, y + size//2)
|
||||
]
|
||||
jaw_points_lower = [
|
||||
(head_x - size//3, y + size//2 + chomp),
|
||||
(head_x + size//3, y + size//2 + chomp),
|
||||
(head_x + size//2, y + size*2//3 + chomp),
|
||||
(head_x - size//2, y + size*2//3 + chomp)
|
||||
]
|
||||
pygame.draw.polygon(surface, head_color, jaw_points_upper)
|
||||
pygame.draw.polygon(surface, head_color, jaw_points_lower)
|
||||
|
||||
# Draw teeth
|
||||
teeth_color = WHITE
|
||||
tooth_width = size//8
|
||||
for tooth_x in range(int(head_x - size//3), int(head_x + size//3), tooth_width):
|
||||
# Upper teeth
|
||||
pygame.draw.polygon(surface, teeth_color, [
|
||||
(tooth_x, y + size//2),
|
||||
(tooth_x + tooth_width//2, y + size//2 - size//8),
|
||||
(tooth_x + tooth_width, y + size//2)
|
||||
])
|
||||
# Lower teeth
|
||||
pygame.draw.polygon(surface, teeth_color, [
|
||||
(tooth_x, y + size//2 + chomp),
|
||||
(tooth_x + tooth_width//2, y + size//2 + size//8 + chomp),
|
||||
(tooth_x + tooth_width, y + size//2 + chomp)
|
||||
])
|
||||
|
||||
def draw_snow_pea(surface, x, y, size):
|
||||
# Get animation offset
|
||||
time = pygame.time.get_ticks()
|
||||
sway = math.sin(time * 0.003) * 3
|
||||
ice_spin = time * 0.003
|
||||
|
||||
# Draw stem with swaying animation
|
||||
stem_points = [
|
||||
(x + size//2 + sway, y + size*2//3),
|
||||
(x + size//2, y + size*7//8),
|
||||
(x + size//2, y + size)
|
||||
]
|
||||
pygame.draw.lines(surface, GREEN, False, stem_points, 3)
|
||||
|
||||
# Draw leaves with frost effect
|
||||
leaf_color = (150, 200, 150)
|
||||
leaf_points = [
|
||||
(x + size//2, y + size*3//4),
|
||||
(x + size//3, y + size*7//8),
|
||||
(x + size//2, y + size*13//16)
|
||||
]
|
||||
pygame.draw.polygon(surface, leaf_color, leaf_points)
|
||||
|
||||
# Draw head with ice effect
|
||||
head_x = x + size//2 + sway
|
||||
head_colors = [(0, 191, 255), (135, 206, 235), (176, 224, 230)]
|
||||
for i, color in enumerate(head_colors):
|
||||
offset = i * 3
|
||||
pygame.draw.ellipse(surface, color,
|
||||
(head_x - size//4 + offset,
|
||||
y + size//4 + offset,
|
||||
size//2 - offset*2,
|
||||
size//2 - offset*2))
|
||||
|
||||
# Draw ice crystals with rotation
|
||||
crystal_color = (200, 232, 255)
|
||||
for i in range(4):
|
||||
angle = ice_spin + i * (math.pi/2)
|
||||
crystal_x = head_x + math.cos(angle) * size//3
|
||||
crystal_y = y + size//2 + math.sin(angle) * size//3
|
||||
crystal_points = [
|
||||
(crystal_x, crystal_y - size//8),
|
||||
(crystal_x + size//8, crystal_y),
|
||||
(crystal_x, crystal_y + size//8),
|
||||
(crystal_x - size//8, crystal_y)
|
||||
]
|
||||
pygame.draw.polygon(surface, crystal_color, crystal_points)
|
||||
|
||||
# Draw frost particles
|
||||
for i in range(3):
|
||||
particle_x = head_x + math.cos(time * 0.001 + i * 2) * size//4
|
||||
particle_y = y + size//2 + math.sin(time * 0.001 + i * 2) * size//4
|
||||
pygame.draw.circle(surface, (255, 255, 255, 128),
|
||||
(int(particle_x), int(particle_y)), size//16)
|
||||
|
||||
def draw_normal_zombie(surface, x, y, size):
|
||||
# Get animation offset
|
||||
time = pygame.time.get_ticks()
|
||||
wobble = math.sin(time * 0.004) * 3
|
||||
|
||||
# Draw shadow
|
||||
shadow_surface = pygame.Surface((size*2//3, size//4), pygame.SRCALPHA)
|
||||
pygame.draw.ellipse(shadow_surface, (0, 0, 0, 64), (0, 0, size*2//3, size//4))
|
||||
surface.blit(shadow_surface, (x + size//6, y + size - size//8))
|
||||
|
||||
# Draw legs with walking animation
|
||||
leg_color = (100, 100, 100)
|
||||
leg_offset = abs(math.sin(time * 0.004)) * 5
|
||||
pygame.draw.line(surface, leg_color,
|
||||
(x + size//2, y + size*2//3),
|
||||
(x + size//3, y + size - leg_offset), 4)
|
||||
pygame.draw.line(surface, leg_color,
|
||||
(x + size//2, y + size*2//3),
|
||||
(x + size*2//3, y + size - leg_offset), 4)
|
||||
|
||||
# Draw tattered clothes
|
||||
clothes_color = (50, 50, 50)
|
||||
clothes_points = [
|
||||
(x + size//3, y + size//2),
|
||||
(x + size*2//3, y + size//2),
|
||||
(x + size*2//3, y + size*3//4),
|
||||
(x + size//3, y + size*3//4)
|
||||
]
|
||||
pygame.draw.polygon(surface, clothes_color, clothes_points)
|
||||
|
||||
# Draw arms with swaying animation
|
||||
arm_color = (100, 100, 100)
|
||||
arm_sway = math.sin(time * 0.004) * 10
|
||||
pygame.draw.line(surface, arm_color,
|
||||
(x + size//2, y + size//2),
|
||||
(x + size//4 + arm_sway, y + size*2//3), 4)
|
||||
pygame.draw.line(surface, arm_color,
|
||||
(x + size//2, y + size//2),
|
||||
(x + size*3//4 + arm_sway, y + size*2//3), 4)
|
||||
|
||||
# Draw body with details
|
||||
body_color = (169, 169, 169)
|
||||
pygame.draw.ellipse(surface, body_color,
|
||||
(x + size//3 + wobble, y + size//3, size//3, size//2))
|
||||
|
||||
# Draw head with details
|
||||
head_color = (169, 169, 169)
|
||||
pygame.draw.circle(surface, head_color,
|
||||
(int(x + size//2 + wobble), int(y + size//3)), size//4)
|
||||
|
||||
# Draw facial features
|
||||
eye_color = (255, 0, 0) # Red eyes
|
||||
pygame.draw.circle(surface, eye_color,
|
||||
(int(x + size//2 - size//8 + wobble), int(y + size//3)), size//12)
|
||||
pygame.draw.circle(surface, eye_color,
|
||||
(int(x + size//2 + size//8 + wobble), int(y + size//3)), size//12)
|
||||
|
||||
# Draw mouth
|
||||
mouth_color = (100, 0, 0)
|
||||
mouth_points = [
|
||||
(x + size//2 - size//6 + wobble, y + size//3 + size//6),
|
||||
(x + size//2 + size//6 + wobble, y + size//3 + size//6),
|
||||
(x + size//2 + wobble, y + size//3 + size//4)
|
||||
]
|
||||
pygame.draw.polygon(surface, mouth_color, mouth_points)
|
||||
|
||||
def draw_cone_zombie(surface, x, y, size):
|
||||
# Draw base zombie
|
||||
draw_normal_zombie(surface, x, y, size)
|
||||
|
||||
# Get animation offset
|
||||
time = pygame.time.get_ticks()
|
||||
wobble = math.sin(time * 0.004) * 3
|
||||
|
||||
# Draw cone with details and shading
|
||||
cone_colors = [(139, 69, 19), (160, 82, 45), (205, 133, 63)] # Different shades of brown
|
||||
for i, color in enumerate(cone_colors):
|
||||
offset = i * 2
|
||||
points = [
|
||||
(x + size//2 + wobble, y - size//6 + offset),
|
||||
(x + size//3 + offset, y + size//3),
|
||||
(x + size*2//3 - offset, y + size//3)
|
||||
]
|
||||
pygame.draw.polygon(surface, color, points)
|
||||
|
||||
# Draw cone damage (dents and scratches)
|
||||
scratch_color = (101, 67, 33)
|
||||
scratch_points = [
|
||||
(x + size//2 - size//8 + wobble, y + size//6),
|
||||
(x + size//2 + size//8 + wobble, y + size//4)
|
||||
]
|
||||
pygame.draw.lines(surface, scratch_color, False, scratch_points, 2)
|
||||
|
||||
def draw_bucket_zombie(surface, x, y, size):
|
||||
# Draw base zombie
|
||||
draw_normal_zombie(surface, x, y, size)
|
||||
|
||||
# Get animation offset
|
||||
time = pygame.time.get_ticks()
|
||||
wobble = math.sin(time * 0.004) * 3
|
||||
|
||||
# Draw bucket with metallic effect
|
||||
bucket_colors = [(192, 192, 192), (169, 169, 169), (128, 128, 128)]
|
||||
for i, color in enumerate(bucket_colors):
|
||||
offset = i * 2
|
||||
pygame.draw.rect(surface, color,
|
||||
(x + size//4 + offset + wobble,
|
||||
y - size//6 + offset,
|
||||
size//2 - offset*2,
|
||||
size//3))
|
||||
|
||||
# Draw bucket rim
|
||||
rim_color = (211, 211, 211)
|
||||
pygame.draw.rect(surface, rim_color,
|
||||
(x + size//4 - 2 + wobble, y - size//6, size//2 + 4, 4))
|
||||
|
||||
# Draw bucket highlights
|
||||
highlight_color = (255, 255, 255)
|
||||
pygame.draw.line(surface, highlight_color,
|
||||
(x + size//3 + wobble, y),
|
||||
(x + size*2//3 + wobble, y), 2)
|
||||
|
||||
# Draw dents and damage
|
||||
dent_color = (128, 128, 128)
|
||||
pygame.draw.arc(surface, dent_color,
|
||||
(x + size//3 + wobble, y, size//4, size//6),
|
||||
0, math.pi, 2)
|
||||
|
||||
def draw_newspaper_zombie(surface, x, y, size):
|
||||
# Draw base zombie
|
||||
draw_normal_zombie(surface, x, y, size)
|
||||
|
||||
# Get animation offset
|
||||
time = pygame.time.get_ticks()
|
||||
wobble = math.sin(time * 0.004) * 3
|
||||
paper_shake = math.sin(time * 0.008) * 2
|
||||
|
||||
# Draw newspaper with animated shaking
|
||||
paper_color = (255, 255, 255)
|
||||
pygame.draw.rect(surface, paper_color,
|
||||
(x + size//6 + paper_shake,
|
||||
y + size//3,
|
||||
size//2,
|
||||
size//2))
|
||||
|
||||
# Draw newspaper content (headlines and text)
|
||||
text_color = (0, 0, 0)
|
||||
for i in range(4):
|
||||
y_pos = y + size//3 + i*size//8
|
||||
pygame.draw.line(surface, text_color,
|
||||
(x + size//5 + paper_shake, y_pos),
|
||||
(x + size*2//3 + paper_shake, y_pos), 1)
|
||||
|
||||
# Draw newspaper damage
|
||||
if time % 2000 < 1000: # Animate paper damage
|
||||
tear_points = [
|
||||
(x + size//3 + paper_shake, y + size//3),
|
||||
(x + size//2 + paper_shake, y + size//2),
|
||||
(x + size//3 + paper_shake, y + size*2//3)
|
||||
]
|
||||
pygame.draw.lines(surface, (200, 200, 200), False, tear_points, 2)
|
||||
|
||||
def draw_dancing_zombie(surface, x, y, size):
|
||||
# Get animation offset
|
||||
time = pygame.time.get_ticks()
|
||||
dance_move = math.sin(time * 0.006) * 10
|
||||
spin = math.sin(time * 0.003) * 0.3
|
||||
|
||||
# Draw shadow
|
||||
shadow_surface = pygame.Surface((size*2//3, size//4), pygame.SRCALPHA)
|
||||
pygame.draw.ellipse(shadow_surface, (0, 0, 0, 64), (0, 0, size*2//3, size//4))
|
||||
surface.blit(shadow_surface, (x + size//6, y + size - size//8))
|
||||
|
||||
# Draw legs in dancing pose
|
||||
leg_color = (100, 100, 100)
|
||||
pygame.draw.line(surface, leg_color,
|
||||
(x + size//2, y + size//2),
|
||||
(x + size//4 + dance_move, y + size), 4)
|
||||
pygame.draw.line(surface, leg_color,
|
||||
(x + size//2, y + size//2),
|
||||
(x + size*3//4 - dance_move, y + size), 4)
|
||||
|
||||
# Draw disco outfit
|
||||
outfit_color = (148, 0, 211) # Purple
|
||||
outfit_points = [
|
||||
(x + size//3 + dance_move/2, y + size//3),
|
||||
(x + size*2//3 + dance_move/2, y + size//3),
|
||||
(x + size*2//3 - dance_move/2, y + size*3//4),
|
||||
(x + size//3 - dance_move/2, y + size*3//4)
|
||||
]
|
||||
pygame.draw.polygon(surface, outfit_color, outfit_points)
|
||||
|
||||
# Draw arms in dancing pose
|
||||
arm_color = (100, 100, 100)
|
||||
pygame.draw.line(surface, arm_color,
|
||||
(x + size//2, y + size//2),
|
||||
(x + size//4 - dance_move, y + size//3), 4)
|
||||
pygame.draw.line(surface, arm_color,
|
||||
(x + size//2, y + size//2),
|
||||
(x + size*3//4 + dance_move, y + size//3), 4)
|
||||
|
||||
# Draw body with disco moves
|
||||
body_color = (169, 169, 169)
|
||||
body_rect = pygame.Rect(x + size//3 + dance_move/2, y + size//4,
|
||||
size//3, size//2)
|
||||
rotated_surface = pygame.Surface((size, size), pygame.SRCALPHA)
|
||||
pygame.draw.ellipse(rotated_surface, body_color, body_rect)
|
||||
|
||||
# Draw head with cool hair
|
||||
head_color = (169, 169, 169)
|
||||
pygame.draw.circle(rotated_surface, head_color,
|
||||
(int(x + size//2 + dance_move/2), int(y + size//3)),
|
||||
size//4)
|
||||
|
||||
# Draw spiky hair with animation
|
||||
hair_color = (0, 0, 0)
|
||||
for i in range(6):
|
||||
angle = i * math.pi/3 + spin
|
||||
hair_x = x + size//2 + math.cos(angle) * size//3 + dance_move/2
|
||||
hair_y = y + size//3 + math.sin(angle) * size//4
|
||||
pygame.draw.line(surface, hair_color,
|
||||
(x + size//2 + dance_move/2, y + size//3),
|
||||
(hair_x, hair_y), 3)
|
||||
|
||||
# Draw sunglasses
|
||||
glasses_color = (0, 0, 0)
|
||||
pygame.draw.rect(surface, glasses_color,
|
||||
(x + size//3 + dance_move/2, y + size//4,
|
||||
size//3, size//8))
|
||||
|
||||
# Draw disco ball effect
|
||||
for i in range(8):
|
||||
angle = i * math.pi/4 + time * 0.01
|
||||
sparkle_x = x + size//2 + math.cos(angle) * size//2
|
||||
sparkle_y = y + size//3 + math.sin(angle) * size//2
|
||||
pygame.draw.circle(surface, (255, 255, 255),
|
||||
(int(sparkle_x), int(sparkle_y)), 2)
|
||||
|
||||
def draw_rose_shooter(surface, x, y, size):
|
||||
# Get animation offset
|
||||
time = pygame.time.get_ticks()
|
||||
sway = math.sin(time * 0.003) * 3
|
||||
petal_spin = time * 0.002
|
||||
|
||||
# Draw stem with swaying animation
|
||||
stem_points = [
|
||||
(x + size//2 + sway, y + size*2//3),
|
||||
(x + size//2, y + size*7//8),
|
||||
(x + size//2, y + size)
|
||||
]
|
||||
pygame.draw.lines(surface, GREEN, False, stem_points, 3)
|
||||
|
||||
# Draw leaves
|
||||
leaf_color = (34, 139, 34) # Forest green
|
||||
leaf_points = [
|
||||
(x + size//2, y + size*3//4),
|
||||
(x + size//3, y + size*7//8),
|
||||
(x + size//2, y + size*13//16)
|
||||
]
|
||||
pygame.draw.polygon(surface, leaf_color, leaf_points)
|
||||
|
||||
# Draw thorns
|
||||
thorn_color = (139, 69, 19) # Brown
|
||||
thorn_points = [
|
||||
[(x + size//2 - 5, y + size*3//4), (x + size//2 - 10, y + size*3//4 - 5), (x + size//2 - 5, y + size*3//4 - 5)],
|
||||
[(x + size//2 + 5, y + size*3//4), (x + size//2 + 10, y + size*3//4 - 5), (x + size//2 + 5, y + size*3//4 - 5)]
|
||||
]
|
||||
for points in thorn_points:
|
||||
pygame.draw.polygon(surface, thorn_color, points)
|
||||
|
||||
# Draw rose head with gradient
|
||||
head_x = x + size//2 + sway
|
||||
head_y = y + size//2
|
||||
rose_colors = [(255, 192, 203), (255, 182, 193), (255, 105, 180)] # Pink gradients
|
||||
|
||||
# Draw petals in layers
|
||||
for i, color in enumerate(rose_colors):
|
||||
offset = i * 3
|
||||
for angle in range(0, 360, 45):
|
||||
rad = math.radians(angle + petal_spin)
|
||||
petal_x = head_x + math.cos(rad) * (size//4 - offset)
|
||||
petal_y = head_y + math.sin(rad) * (size//4 - offset)
|
||||
pygame.draw.circle(surface, color, (int(petal_x), int(petal_y)), size//6 - offset)
|
||||
|
||||
# Draw center
|
||||
pygame.draw.circle(surface, (139, 0, 0), (int(head_x), int(head_y)), size//8) # Dark red center
|
||||
|
||||
# Draw shooter with highlight
|
||||
shooter_x = head_x + size//4
|
||||
shooter_color = (255, 20, 147) # Deep pink
|
||||
pygame.draw.circle(surface, shooter_color, (int(shooter_x), head_y), size//7)
|
||||
pygame.draw.circle(surface, (255, 105, 180), (int(shooter_x), head_y), size//10) # Highlight
|
||||
|
||||
# Dictionary mapping plant types to their drawing functions
|
||||
PLANT_DRAWINGS = {
|
||||
PlantType.SUNFLOWER: draw_sunflower,
|
||||
PlantType.PEASHOOTER: draw_peashooter,
|
||||
PlantType.ROSE_SHOOTER: draw_rose_shooter,
|
||||
PlantType.CHOMPER: draw_chomper,
|
||||
PlantType.SNOW_PEA: draw_snow_pea
|
||||
}
|
||||
|
||||
# Dictionary mapping zombie types to their drawing functions
|
||||
ZOMBIE_DRAWINGS = {
|
||||
ZombieType.NORMAL: draw_normal_zombie,
|
||||
ZombieType.CONE: draw_cone_zombie,
|
||||
ZombieType.BUCKET: draw_bucket_zombie,
|
||||
ZombieType.NEWSPAPER: draw_newspaper_zombie,
|
||||
ZombieType.DANCING: draw_dancing_zombie
|
||||
}
|
||||
251
WareHouse/snake_THUNLPDemo_2024/main.py
Normal file
@ -0,0 +1,251 @@
|
||||
import pygame
|
||||
import random
|
||||
import sys
|
||||
import math
|
||||
|
||||
# Initialize Pygame
|
||||
pygame.init()
|
||||
|
||||
# Colors
|
||||
WHITE = (255, 255, 255)
|
||||
RED = (255, 50, 50)
|
||||
GREEN = (50, 255, 50)
|
||||
BLUE = (50, 50, 255)
|
||||
BLACK = (0, 0, 0)
|
||||
DARK_GREEN = (34, 139, 34)
|
||||
GOLD = (255, 215, 0)
|
||||
|
||||
# Game settings
|
||||
WINDOW_SIZE = 800
|
||||
GRID_SIZE = 20
|
||||
GRID_COUNT = WINDOW_SIZE // GRID_SIZE
|
||||
GAME_SPEED = 10
|
||||
|
||||
# Create window
|
||||
screen = pygame.display.set_mode((WINDOW_SIZE, WINDOW_SIZE))
|
||||
pygame.display.set_caption('贪吃蛇')
|
||||
clock = pygame.time.Clock()
|
||||
|
||||
# Load and create background texture
|
||||
background = pygame.Surface((WINDOW_SIZE, WINDOW_SIZE))
|
||||
for y in range(0, WINDOW_SIZE, 4):
|
||||
for x in range(0, WINDOW_SIZE, 4):
|
||||
shade = random.randint(0, 20)
|
||||
pygame.draw.rect(background, (shade, shade, shade), (x, y, 4, 4))
|
||||
|
||||
class Particle:
|
||||
def __init__(self, x, y):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.vx = random.uniform(-2, 2)
|
||||
self.vy = random.uniform(-2, 2)
|
||||
self.lifetime = 30
|
||||
self.color = (random.randint(200, 255), random.randint(200, 255), random.randint(0, 50))
|
||||
|
||||
def update(self):
|
||||
self.x += self.vx
|
||||
self.y += self.vy
|
||||
self.lifetime -= 1
|
||||
|
||||
def draw(self, surface):
|
||||
alpha = int((self.lifetime / 30) * 255)
|
||||
particle_surface = pygame.Surface((4, 4), pygame.SRCALPHA)
|
||||
particle_surface.fill((*self.color, alpha))
|
||||
surface.blit(particle_surface, (int(self.x), int(self.y)))
|
||||
|
||||
class Snake:
|
||||
def __init__(self):
|
||||
self.body = [(GRID_COUNT//2, GRID_COUNT//2)]
|
||||
self.direction = (1, 0)
|
||||
self.grow = False
|
||||
self.angle = 0 # For snake movement animation
|
||||
|
||||
def move(self):
|
||||
head = self.body[0]
|
||||
new_head = (head[0] + self.direction[0], head[1] + self.direction[1])
|
||||
|
||||
if not self.grow:
|
||||
self.body.pop()
|
||||
else:
|
||||
self.grow = False
|
||||
|
||||
self.body.insert(0, new_head)
|
||||
self.angle += 0.2 # Update movement animation
|
||||
|
||||
def draw(self):
|
||||
for i, segment in enumerate(self.body):
|
||||
x = segment[0] * GRID_SIZE
|
||||
y = segment[1] * GRID_SIZE
|
||||
|
||||
# Create snake skin pattern with gradient
|
||||
base_color = (34, max(50, 255 - (i * 8)), 34)
|
||||
|
||||
# Add wave effect to snake body
|
||||
offset = math.sin(self.angle + i * 0.3) * 2
|
||||
|
||||
# Draw main body segment with gradient
|
||||
pygame.draw.rect(screen, base_color, (x, y + offset, GRID_SIZE-2, GRID_SIZE-2))
|
||||
|
||||
# Add scale pattern
|
||||
if i > 0:
|
||||
scale_color = (max(20, base_color[0] - 20),
|
||||
max(20, base_color[1] - 20),
|
||||
max(20, base_color[2] - 20))
|
||||
pygame.draw.arc(screen, scale_color,
|
||||
(x + 2, y + offset + 2, GRID_SIZE-6, GRID_SIZE-6),
|
||||
0, 3.14, 2)
|
||||
|
||||
# Draw head with special details
|
||||
if i == 0:
|
||||
# Draw eyes with shine effect
|
||||
eye_size = GRID_SIZE // 4
|
||||
# Left eye
|
||||
pygame.draw.circle(screen, WHITE,
|
||||
(x + GRID_SIZE//3, y + offset + GRID_SIZE//3), eye_size)
|
||||
pygame.draw.circle(screen, BLACK,
|
||||
(x + GRID_SIZE//3, y + offset + GRID_SIZE//3), eye_size//2)
|
||||
pygame.draw.circle(screen, WHITE,
|
||||
(x + GRID_SIZE//3 - 1, y + offset + GRID_SIZE//3 - 1), eye_size//4)
|
||||
|
||||
# Right eye
|
||||
pygame.draw.circle(screen, WHITE,
|
||||
(x + 2*GRID_SIZE//3, y + offset + GRID_SIZE//3), eye_size)
|
||||
pygame.draw.circle(screen, BLACK,
|
||||
(x + 2*GRID_SIZE//3, y + offset + GRID_SIZE//3), eye_size//2)
|
||||
pygame.draw.circle(screen, WHITE,
|
||||
(x + 2*GRID_SIZE//3 - 1, y + offset + GRID_SIZE//3 - 1), eye_size//4)
|
||||
|
||||
class Food:
|
||||
def __init__(self):
|
||||
self.position = self.get_random_position()
|
||||
self.angle = 0
|
||||
self.particles = []
|
||||
|
||||
def get_random_position(self):
|
||||
return (random.randint(0, GRID_COUNT-1), random.randint(0, GRID_COUNT-1))
|
||||
|
||||
def update(self):
|
||||
self.angle += 0.1
|
||||
|
||||
# Update particles
|
||||
self.particles = [p for p in self.particles if p.lifetime > 0]
|
||||
for particle in self.particles:
|
||||
particle.update()
|
||||
|
||||
def draw(self):
|
||||
x = self.position[0] * GRID_SIZE
|
||||
y = self.position[1] * GRID_SIZE
|
||||
|
||||
# Draw particles
|
||||
for particle in self.particles:
|
||||
particle.draw(screen)
|
||||
|
||||
# Draw apple with pulsing effect
|
||||
size_mod = math.sin(self.angle) * 2
|
||||
apple_size = GRID_SIZE//2 - 2 + size_mod
|
||||
|
||||
# Draw apple shadow
|
||||
shadow_pos = (x + GRID_SIZE//2 + 2, y + GRID_SIZE//2 + 2)
|
||||
pygame.draw.circle(screen, (20, 20, 20), shadow_pos, apple_size)
|
||||
|
||||
# Draw apple body
|
||||
apple_pos = (x + GRID_SIZE//2, y + GRID_SIZE//2)
|
||||
pygame.draw.circle(screen, RED, apple_pos, apple_size)
|
||||
|
||||
# Draw apple highlight
|
||||
highlight_pos = (x + GRID_SIZE//2 - 2, y + GRID_SIZE//2 - 2)
|
||||
pygame.draw.circle(screen, (255, 150, 150), highlight_pos, apple_size//3)
|
||||
|
||||
# Draw leaf with animation
|
||||
leaf_x = x + GRID_SIZE//2 + math.sin(self.angle) * 2
|
||||
leaf_y = y + math.cos(self.angle) * 2
|
||||
pygame.draw.ellipse(screen, GREEN, (leaf_x, leaf_y, GRID_SIZE//4, GRID_SIZE//3))
|
||||
|
||||
def draw_title_and_score(score):
|
||||
# Draw game title
|
||||
title_font = pygame.font.Font(None, 74)
|
||||
title_text = title_font.render('Snake Game', True, GOLD)
|
||||
title_shadow = title_font.render('Snake Game', True, (50, 50, 50))
|
||||
|
||||
# Add shadow effect
|
||||
screen.blit(title_shadow, (WINDOW_SIZE//2 - title_text.get_width()//2 + 2,
|
||||
42))
|
||||
screen.blit(title_text, (WINDOW_SIZE//2 - title_text.get_width()//2,
|
||||
40))
|
||||
|
||||
# Draw score with fancy styling
|
||||
score_font = pygame.font.Font(None, 48)
|
||||
score_text = score_font.render(f'Score: {score}', True, WHITE)
|
||||
score_shadow = score_font.render(f'Score: {score}', True, (50, 50, 50))
|
||||
|
||||
screen.blit(score_shadow, (12, 12))
|
||||
screen.blit(score_text, (10, 10))
|
||||
|
||||
def main():
|
||||
snake = Snake()
|
||||
food = Food()
|
||||
score = 0
|
||||
|
||||
while True:
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_UP and snake.direction != (0, 1):
|
||||
snake.direction = (0, -1)
|
||||
if event.key == pygame.K_DOWN and snake.direction != (0, -1):
|
||||
snake.direction = (0, 1)
|
||||
if event.key == pygame.K_LEFT and snake.direction != (1, 0):
|
||||
snake.direction = (-1, 0)
|
||||
if event.key == pygame.K_RIGHT and snake.direction != (-1, 0):
|
||||
snake.direction = (1, 0)
|
||||
|
||||
# Move snake
|
||||
snake.move()
|
||||
|
||||
# Update food animation
|
||||
food.update()
|
||||
|
||||
# Check collision with food
|
||||
if snake.body[0] == food.position:
|
||||
snake.grow = True
|
||||
food.position = food.get_random_position()
|
||||
score += 1
|
||||
# Add particles on food collection
|
||||
x = food.position[0] * GRID_SIZE
|
||||
y = food.position[1] * GRID_SIZE
|
||||
for _ in range(20):
|
||||
food.particles.append(Particle(x + GRID_SIZE//2, y + GRID_SIZE//2))
|
||||
|
||||
# Check collision with walls
|
||||
head = snake.body[0]
|
||||
if head[0] < 0 or head[0] >= GRID_COUNT or head[1] < 0 or head[1] >= GRID_COUNT:
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
|
||||
# Check collision with self
|
||||
if head in snake.body[1:]:
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
|
||||
# Draw everything
|
||||
screen.blit(background, (0, 0))
|
||||
|
||||
# Draw grid lines with fade effect
|
||||
for i in range(GRID_COUNT):
|
||||
alpha = abs(math.sin(i * 0.1 + pygame.time.get_ticks() * 0.001)) * 30 + 20
|
||||
grid_surface = pygame.Surface((WINDOW_SIZE, 1), pygame.SRCALPHA)
|
||||
grid_surface.fill((50, 50, 50, int(alpha)))
|
||||
screen.blit(grid_surface, (0, i * GRID_SIZE))
|
||||
screen.blit(grid_surface, (i * GRID_SIZE, 0))
|
||||
|
||||
snake.draw()
|
||||
food.draw()
|
||||
draw_title_and_score(score)
|
||||
|
||||
pygame.display.flip()
|
||||
clock.tick(GAME_SPEED)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
35
WareHouse/tetris_THUNLPDemo_2024/README.md
Normal file
@ -0,0 +1,35 @@
|
||||
# Modern Tetris Game
|
||||
|
||||
A feature-rich Tetris implementation in Python using Pygame.
|
||||
|
||||
## Features
|
||||
- Modern GUI with smooth animations
|
||||
- Score system and levels
|
||||
- Ghost piece preview
|
||||
- Next piece preview
|
||||
- Hold piece functionality
|
||||
- Particle effects for line clears
|
||||
- Background music and sound effects
|
||||
- High score system
|
||||
|
||||
## Controls
|
||||
- Left/Right Arrow: Move piece
|
||||
- Up Arrow: Rotate piece clockwise
|
||||
- Z: Rotate piece counter-clockwise
|
||||
- Down Arrow: Soft drop
|
||||
- Space: Hard drop
|
||||
- C: Hold piece
|
||||
- P: Pause game
|
||||
- ESC: Quit game
|
||||
|
||||
## Installation
|
||||
1. Install Python 3.8+
|
||||
2. Install dependencies:
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
3. Run the game:
|
||||
```bash
|
||||
python tetris.py
|
||||
```
|
||||
1
WareHouse/tetris_THUNLPDemo_2024/highscore.txt
Normal file
@ -0,0 +1 @@
|
||||
700
|
||||
680
WareHouse/tetris_THUNLPDemo_2024/main.py
Normal file
@ -0,0 +1,680 @@
|
||||
import pygame
|
||||
import random
|
||||
import numpy as np
|
||||
from typing import List, Tuple, Optional
|
||||
import os
|
||||
import math
|
||||
import time
|
||||
|
||||
# Initialize Pygame
|
||||
pygame.init()
|
||||
pygame.mixer.init()
|
||||
|
||||
# Constants
|
||||
BLOCK_SIZE = 30
|
||||
GRID_WIDTH = 10
|
||||
GRID_HEIGHT = 20
|
||||
PREVIEW_SIZE = 4
|
||||
|
||||
# Calculate window size
|
||||
SIDE_PANEL_WIDTH = 200
|
||||
WINDOW_WIDTH = BLOCK_SIZE * GRID_WIDTH + SIDE_PANEL_WIDTH
|
||||
WINDOW_HEIGHT = BLOCK_SIZE * GRID_HEIGHT
|
||||
|
||||
# Colors
|
||||
BLACK = (0, 0, 0)
|
||||
WHITE = (255, 255, 255)
|
||||
GRAY = (128, 128, 128)
|
||||
COLORS = {
|
||||
'I': (0, 255, 255), # Cyan
|
||||
'O': (255, 255, 0), # Yellow
|
||||
'T': (128, 0, 128), # Purple
|
||||
'S': (0, 255, 0), # Green
|
||||
'Z': (255, 0, 0), # Red
|
||||
'J': (0, 0, 255), # Blue
|
||||
'L': (255, 165, 0), # Orange
|
||||
}
|
||||
|
||||
# Game settings
|
||||
INITIAL_FALL_SPEED = 0.8 # Initial time between falls in seconds
|
||||
SOFT_DROP_SPEED = 0.05 # Time between falls when soft dropping
|
||||
SPEED_UP_FACTOR = 0.08 # How much to speed up per level
|
||||
MIN_FALL_SPEED = 0.1 # Minimum fall speed
|
||||
LOCK_DELAY = 0.5 # Time in seconds before piece locks in place
|
||||
MAX_LOCK_RESETS = 15 # Maximum number of lock delay resets
|
||||
|
||||
# Animation settings
|
||||
MOVE_ANIMATION_SPEED = 0.05 # seconds (faster horizontal movement)
|
||||
ROTATION_ANIMATION_SPEED = 0.08 # seconds
|
||||
LINE_CLEAR_ANIMATION_TIME = 0.3 # seconds
|
||||
FLASH_SPEED = 0.05 # seconds
|
||||
|
||||
# Tetromino shapes
|
||||
SHAPES = {
|
||||
'I': [[1, 1, 1, 1]],
|
||||
'O': [[1, 1], [1, 1]],
|
||||
'T': [[0, 1, 0], [1, 1, 1]],
|
||||
'S': [[0, 1, 1], [1, 1, 0]],
|
||||
'Z': [[1, 1, 0], [0, 1, 1]],
|
||||
'J': [[1, 0, 0], [1, 1, 1]],
|
||||
'L': [[0, 0, 1], [1, 1, 1]]
|
||||
}
|
||||
|
||||
class AnimationState:
|
||||
def __init__(self):
|
||||
self.move_progress = 0
|
||||
self.rotation_progress = 0
|
||||
self.line_clear_progress = 0
|
||||
self.flash_progress = 0
|
||||
self.last_pos = None
|
||||
self.last_shape = None
|
||||
self.target_pos = None
|
||||
self.target_shape = None
|
||||
self.lines_being_cleared = []
|
||||
self.flash_active = False
|
||||
|
||||
class Particle:
|
||||
def __init__(self, x: int, y: int, color: Tuple[int, int, int]):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.color = color
|
||||
self.velocity = [random.uniform(-3, 3), random.uniform(-8, -4)]
|
||||
self.life = 255
|
||||
self.size = random.randint(2, 6)
|
||||
self.rotation = random.uniform(0, 360)
|
||||
self.rotation_speed = random.uniform(-5, 5)
|
||||
|
||||
def update(self):
|
||||
self.x += self.velocity[0]
|
||||
self.y += self.velocity[1]
|
||||
self.velocity[1] += 0.2 # Gravity
|
||||
self.velocity[0] *= 0.99 # Air resistance
|
||||
self.life -= 3
|
||||
self.rotation += self.rotation_speed
|
||||
return self.life > 0
|
||||
|
||||
def draw(self, screen):
|
||||
if self.life <= 0:
|
||||
return
|
||||
|
||||
alpha = max(0, min(255, self.life))
|
||||
color = (*self.color, alpha)
|
||||
|
||||
# Create rotated particle
|
||||
surface = pygame.Surface((self.size * 2, self.size * 2), pygame.SRCALPHA)
|
||||
points = [
|
||||
(self.size + math.cos(math.radians(self.rotation)) * self.size,
|
||||
self.size + math.sin(math.radians(self.rotation)) * self.size),
|
||||
(self.size + math.cos(math.radians(self.rotation + 120)) * self.size,
|
||||
self.size + math.sin(math.radians(self.rotation + 120)) * self.size),
|
||||
(self.size + math.cos(math.radians(self.rotation + 240)) * self.size,
|
||||
self.size + math.sin(math.radians(self.rotation + 240)) * self.size)
|
||||
]
|
||||
pygame.draw.polygon(surface, color, points)
|
||||
screen.blit(surface, (self.x - self.size, self.y - self.size))
|
||||
|
||||
class Tetris:
|
||||
def __init__(self):
|
||||
self.screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
|
||||
pygame.display.set_caption('俄罗斯方块')
|
||||
|
||||
self.clock = pygame.time.Clock()
|
||||
self.grid = [[None for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)]
|
||||
self.current_piece = None
|
||||
self.current_shape = None
|
||||
self.current_pos = None
|
||||
self.held_piece = None
|
||||
self.can_hold = True
|
||||
self.next_piece = self._get_random_piece()
|
||||
self.game_over = False
|
||||
self.score = 0
|
||||
self.level = 1
|
||||
self.lines_cleared = 0
|
||||
self.particles = []
|
||||
self.fall_speed = INITIAL_FALL_SPEED
|
||||
self.current_fall_speed = INITIAL_FALL_SPEED
|
||||
self.last_fall_time = time.time()
|
||||
self.lock_delay_time = 0
|
||||
self.lock_delay_active = False
|
||||
self.lock_reset_count = 0
|
||||
self.last_move_time = time.time()
|
||||
self.paused = False
|
||||
self.combo = 0
|
||||
self.force_down = False # New flag for forcing piece down
|
||||
|
||||
# Animation state
|
||||
self.animation = AnimationState()
|
||||
|
||||
# Load high score
|
||||
self.high_score = self._load_high_score()
|
||||
|
||||
# Initialize fonts
|
||||
self.font_big = pygame.font.Font(None, 48)
|
||||
self.font_small = pygame.font.Font(None, 36)
|
||||
|
||||
# Load sounds
|
||||
self._load_sounds()
|
||||
|
||||
# Background gradient
|
||||
self.background = self._create_background()
|
||||
|
||||
def _load_sounds(self):
|
||||
# Create sounds directory if it doesn't exist
|
||||
if not os.path.exists('sounds'):
|
||||
os.makedirs('sounds')
|
||||
|
||||
# Initialize empty/silent sounds
|
||||
empty_sound = pygame.mixer.Sound(buffer=bytes([0]*44)) # Minimal silent sound
|
||||
self.sounds = {
|
||||
'move': empty_sound,
|
||||
'rotate': empty_sound,
|
||||
'drop': empty_sound,
|
||||
'clear': empty_sound,
|
||||
'game_over': empty_sound
|
||||
}
|
||||
|
||||
def _create_background(self):
|
||||
surface = pygame.Surface((WINDOW_WIDTH, WINDOW_HEIGHT))
|
||||
for y in range(WINDOW_HEIGHT):
|
||||
progress = y / WINDOW_HEIGHT
|
||||
color = (
|
||||
int(20 + 20 * math.sin(progress * math.pi)),
|
||||
int(10 + 10 * math.sin(progress * math.pi * 2)),
|
||||
int(40 + 20 * math.sin(progress * math.pi * 0.5))
|
||||
)
|
||||
pygame.draw.line(surface, color, (0, y), (WINDOW_WIDTH, y))
|
||||
return surface
|
||||
|
||||
def _load_high_score(self) -> int:
|
||||
try:
|
||||
if os.path.exists('highscore.txt'):
|
||||
with open('highscore.txt', 'r') as f:
|
||||
return int(f.read())
|
||||
except:
|
||||
pass
|
||||
return 0
|
||||
|
||||
def _save_high_score(self):
|
||||
with open('highscore.txt', 'w') as f:
|
||||
f.write(str(self.high_score))
|
||||
|
||||
def _get_random_piece(self) -> str:
|
||||
return random.choice(list(SHAPES.keys()))
|
||||
|
||||
def new_piece(self):
|
||||
self.current_piece = self.next_piece
|
||||
self.next_piece = self._get_random_piece()
|
||||
self.current_shape = SHAPES[self.current_piece]
|
||||
self.current_pos = [0, GRID_WIDTH//2 - len(self.current_shape[0])//2]
|
||||
self.can_hold = True
|
||||
|
||||
# Reset animation state
|
||||
self.animation.move_progress = 0
|
||||
self.animation.rotation_progress = 0
|
||||
self.animation.last_pos = self.current_pos.copy()
|
||||
self.animation.last_shape = [row[:] for row in self.current_shape]
|
||||
self.animation.target_pos = self.current_pos.copy()
|
||||
self.animation.target_shape = [row[:] for row in self.current_shape]
|
||||
|
||||
# Check if game over
|
||||
if self._check_collision():
|
||||
self.game_over = True
|
||||
self.sounds['game_over'].play()
|
||||
|
||||
def hold_piece(self):
|
||||
if not self.can_hold:
|
||||
return
|
||||
|
||||
self.sounds['rotate'].play()
|
||||
|
||||
if self.held_piece is None:
|
||||
self.held_piece = self.current_piece
|
||||
self.new_piece()
|
||||
else:
|
||||
self.held_piece, self.current_piece = self.current_piece, self.held_piece
|
||||
self.current_shape = SHAPES[self.current_piece]
|
||||
self.current_pos = [0, GRID_WIDTH//2 - len(self.current_shape[0])//2]
|
||||
|
||||
self.can_hold = False
|
||||
|
||||
def rotate_piece(self, clockwise: bool = True):
|
||||
if self.current_piece == 'O':
|
||||
return
|
||||
|
||||
self.sounds['rotate'].play()
|
||||
|
||||
old_shape = self.current_shape
|
||||
self.current_shape = np.rot90(self.current_shape, 1 if not clockwise else -1).tolist()
|
||||
|
||||
# Update animation state
|
||||
self.animation.last_shape = old_shape
|
||||
self.animation.target_shape = self.current_shape
|
||||
self.animation.rotation_progress = 0
|
||||
|
||||
if self._check_collision():
|
||||
self.current_shape = old_shape
|
||||
self.animation.target_shape = old_shape
|
||||
|
||||
def _check_collision(self) -> bool:
|
||||
for y, row in enumerate(self.current_shape):
|
||||
for x, cell in enumerate(row):
|
||||
if cell:
|
||||
grid_y = self.current_pos[0] + y
|
||||
grid_x = self.current_pos[1] + x
|
||||
|
||||
if (grid_x < 0 or grid_x >= GRID_WIDTH or
|
||||
grid_y >= GRID_HEIGHT or
|
||||
(grid_y >= 0 and self.grid[grid_y][grid_x] is not None)):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _get_ghost_position(self) -> List[int]:
|
||||
ghost_pos = self.current_pos.copy()
|
||||
temp = self.current_pos.copy()
|
||||
|
||||
while True:
|
||||
ghost_pos[0] += 1
|
||||
self.current_pos = ghost_pos.copy()
|
||||
if self._check_collision():
|
||||
ghost_pos[0] -= 1
|
||||
self.current_pos = temp
|
||||
break
|
||||
return ghost_pos
|
||||
|
||||
def move(self, dx: int, dy: int):
|
||||
old_pos = self.current_pos.copy()
|
||||
self.current_pos[1] += dx
|
||||
self.current_pos[0] += dy
|
||||
|
||||
if dx != 0:
|
||||
self.sounds['move'].play()
|
||||
|
||||
collision = self._check_collision()
|
||||
if collision:
|
||||
self.current_pos[1] -= dx
|
||||
self.current_pos[0] -= dy
|
||||
|
||||
if dy > 0: # If moving down caused collision
|
||||
if not self.lock_delay_active:
|
||||
# Start lock delay when piece first touches ground
|
||||
self.lock_delay_active = True
|
||||
self.lock_delay_time = time.time()
|
||||
self.lock_reset_count = 0
|
||||
elif time.time() - self.lock_delay_time > LOCK_DELAY or self.force_down:
|
||||
# Lock piece if lock delay expired or forced down
|
||||
self._place_piece()
|
||||
self._clear_lines()
|
||||
self.new_piece()
|
||||
self.lock_delay_active = False
|
||||
self.force_down = False
|
||||
return True
|
||||
else:
|
||||
# Reset lock delay if piece moved successfully and still touching ground
|
||||
if self.lock_delay_active and self.lock_reset_count < MAX_LOCK_RESETS:
|
||||
# Check if still touching ground after move
|
||||
self.current_pos[0] += 1
|
||||
if self._check_collision():
|
||||
self.lock_delay_time = time.time()
|
||||
self.lock_reset_count += 1
|
||||
self.current_pos[0] -= 1
|
||||
else:
|
||||
# If piece is not touching ground, deactivate lock delay
|
||||
self.current_pos[0] += 1
|
||||
if not self._check_collision():
|
||||
self.lock_delay_active = False
|
||||
self.current_pos[0] -= 1
|
||||
|
||||
# Update animation state for horizontal movement only
|
||||
if dx != 0:
|
||||
self.animation.last_pos = old_pos
|
||||
self.animation.target_pos = self.current_pos.copy()
|
||||
self.animation.move_progress = 0
|
||||
return False
|
||||
|
||||
def hard_drop(self):
|
||||
self.sounds['drop'].play()
|
||||
ghost_pos = self._get_ghost_position()
|
||||
self.current_pos = ghost_pos
|
||||
self.force_down = True # Force the piece to lock immediately
|
||||
self.move(0, 1) # This will trigger the locking process
|
||||
|
||||
def _place_piece(self):
|
||||
for y, row in enumerate(self.current_shape):
|
||||
for x, cell in enumerate(row):
|
||||
if cell:
|
||||
grid_y = self.current_pos[0] + y
|
||||
grid_x = self.current_pos[1] + x
|
||||
if 0 <= grid_y < GRID_HEIGHT:
|
||||
self.grid[grid_y][grid_x] = self.current_piece
|
||||
|
||||
# Create landing particles
|
||||
for x in range(len(self.current_shape[0])):
|
||||
color = COLORS[self.current_piece]
|
||||
px = (self.current_pos[1] + x) * BLOCK_SIZE
|
||||
py = (self.current_pos[0] + len(self.current_shape) - 1) * BLOCK_SIZE
|
||||
for _ in range(5):
|
||||
self.particles.append(Particle(px, py, color))
|
||||
|
||||
def _clear_lines(self):
|
||||
lines_to_clear = []
|
||||
for y in range(GRID_HEIGHT):
|
||||
if all(cell is not None for cell in self.grid[y]):
|
||||
lines_to_clear.append(y)
|
||||
|
||||
if not lines_to_clear:
|
||||
self.combo = 0
|
||||
return
|
||||
|
||||
self.sounds['clear'].play()
|
||||
self.animation.lines_being_cleared = lines_to_clear
|
||||
self.animation.line_clear_progress = 0
|
||||
self.animation.flash_active = True
|
||||
self.animation.flash_progress = 0
|
||||
|
||||
# Create particles for cleared lines
|
||||
for y in lines_to_clear:
|
||||
for x in range(GRID_WIDTH):
|
||||
color = COLORS[self.grid[y][x]]
|
||||
px = x * BLOCK_SIZE
|
||||
py = y * BLOCK_SIZE
|
||||
for _ in range(5): # 5 particles per block
|
||||
self.particles.append(Particle(px, py, color))
|
||||
|
||||
# Clear lines and update score
|
||||
for y in lines_to_clear:
|
||||
self.grid.pop(y)
|
||||
self.grid.insert(0, [None] * GRID_WIDTH)
|
||||
|
||||
lines_count = len(lines_to_clear)
|
||||
self.lines_cleared += lines_count
|
||||
|
||||
# Calculate score with combo bonus
|
||||
self.combo += 1
|
||||
combo_multiplier = min(self.combo, 10) # Cap combo at 10x
|
||||
base_score = [100, 300, 500, 800][lines_count - 1]
|
||||
self.score += base_score * self.level * combo_multiplier
|
||||
|
||||
self.level = self.lines_cleared // 10 + 1
|
||||
self.fall_speed = max(MIN_FALL_SPEED,
|
||||
INITIAL_FALL_SPEED - (self.level - 1) * SPEED_UP_FACTOR)
|
||||
|
||||
if self.score > self.high_score:
|
||||
self.high_score = self.score
|
||||
self._save_high_score()
|
||||
|
||||
def _interpolate_position(self, progress: float) -> List[int]:
|
||||
if self.animation.last_pos is None or self.animation.target_pos is None:
|
||||
return self.current_pos
|
||||
|
||||
# Only interpolate horizontal movement
|
||||
return [
|
||||
self.current_pos[0], # Vertical position is always current
|
||||
self.animation.last_pos[1] + (self.animation.target_pos[1] - self.animation.last_pos[1]) * progress
|
||||
]
|
||||
|
||||
def draw(self):
|
||||
# Draw background
|
||||
self.screen.blit(self.background, (0, 0))
|
||||
|
||||
# Draw grid
|
||||
for y in range(GRID_HEIGHT):
|
||||
for x in range(GRID_WIDTH):
|
||||
pygame.draw.rect(self.screen, GRAY,
|
||||
(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE), 1)
|
||||
|
||||
# Draw placed pieces
|
||||
for y in range(GRID_HEIGHT):
|
||||
for x in range(GRID_WIDTH):
|
||||
if self.grid[y][x]:
|
||||
if y in self.animation.lines_being_cleared:
|
||||
# Skip drawing blocks in lines being cleared during animation
|
||||
if self.animation.line_clear_progress < LINE_CLEAR_ANIMATION_TIME:
|
||||
continue
|
||||
color = COLORS[self.grid[y][x]]
|
||||
pygame.draw.rect(self.screen, color,
|
||||
(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE))
|
||||
pygame.draw.rect(self.screen, WHITE,
|
||||
(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE), 1)
|
||||
|
||||
# Draw ghost piece
|
||||
if self.current_piece:
|
||||
ghost_pos = self._get_ghost_position()
|
||||
for y, row in enumerate(self.current_shape):
|
||||
for x, cell in enumerate(row):
|
||||
if cell:
|
||||
color = (*COLORS[self.current_piece], 128)
|
||||
ghost_x = (ghost_pos[1] + x) * BLOCK_SIZE
|
||||
ghost_y = (ghost_pos[0] + y) * BLOCK_SIZE
|
||||
surface = pygame.Surface((BLOCK_SIZE, BLOCK_SIZE), pygame.SRCALPHA)
|
||||
pygame.draw.rect(surface, color, (0, 0, BLOCK_SIZE, BLOCK_SIZE))
|
||||
self.screen.blit(surface, (ghost_x, ghost_y))
|
||||
|
||||
# Draw current piece with animation
|
||||
if self.current_piece:
|
||||
pos = self._interpolate_position(min(1, self.animation.move_progress / MOVE_ANIMATION_SPEED))
|
||||
|
||||
for y, row in enumerate(self.current_shape):
|
||||
for x, cell in enumerate(row):
|
||||
if cell:
|
||||
color = COLORS[self.current_piece]
|
||||
block_x = (pos[1] + x) * BLOCK_SIZE
|
||||
block_y = (pos[0] + y) * BLOCK_SIZE
|
||||
|
||||
# Apply rotation animation
|
||||
if self.animation.rotation_progress < ROTATION_ANIMATION_SPEED:
|
||||
progress = self.animation.rotation_progress / ROTATION_ANIMATION_SPEED
|
||||
scale = 1 - math.sin(progress * math.pi) * 0.2
|
||||
|
||||
# Calculate center of rotation
|
||||
center_x = pos[1] * BLOCK_SIZE + len(row) * BLOCK_SIZE / 2
|
||||
center_y = pos[0] * BLOCK_SIZE + len(self.current_shape) * BLOCK_SIZE / 2
|
||||
|
||||
# Adjust block position for rotation
|
||||
block_x = center_x + (block_x - center_x) * scale
|
||||
block_y = center_y + (block_y - center_y) * scale
|
||||
|
||||
pygame.draw.rect(self.screen, color,
|
||||
(block_x, block_y, BLOCK_SIZE, BLOCK_SIZE))
|
||||
pygame.draw.rect(self.screen, WHITE,
|
||||
(block_x, block_y, BLOCK_SIZE, BLOCK_SIZE), 1)
|
||||
|
||||
# Draw side panel
|
||||
panel_x = GRID_WIDTH * BLOCK_SIZE + 10
|
||||
|
||||
# Draw next piece preview
|
||||
next_text = self.font_small.render('Next:', True, WHITE)
|
||||
self.screen.blit(next_text, (panel_x, 20))
|
||||
next_shape = SHAPES[self.next_piece]
|
||||
for y, row in enumerate(next_shape):
|
||||
for x, cell in enumerate(row):
|
||||
if cell:
|
||||
color = COLORS[self.next_piece]
|
||||
pygame.draw.rect(self.screen, color,
|
||||
(panel_x + x * BLOCK_SIZE,
|
||||
60 + y * BLOCK_SIZE,
|
||||
BLOCK_SIZE, BLOCK_SIZE))
|
||||
pygame.draw.rect(self.screen, WHITE,
|
||||
(panel_x + x * BLOCK_SIZE,
|
||||
60 + y * BLOCK_SIZE,
|
||||
BLOCK_SIZE, BLOCK_SIZE), 1)
|
||||
|
||||
# Draw held piece
|
||||
held_text = self.font_small.render('Hold:', True, WHITE)
|
||||
self.screen.blit(held_text, (panel_x, 160))
|
||||
if self.held_piece:
|
||||
held_shape = SHAPES[self.held_piece]
|
||||
for y, row in enumerate(held_shape):
|
||||
for x, cell in enumerate(row):
|
||||
if cell:
|
||||
color = COLORS[self.held_piece]
|
||||
if not self.can_hold:
|
||||
color = tuple(c//2 for c in color) # Darken color
|
||||
pygame.draw.rect(self.screen, color,
|
||||
(panel_x + x * BLOCK_SIZE,
|
||||
200 + y * BLOCK_SIZE,
|
||||
BLOCK_SIZE, BLOCK_SIZE))
|
||||
pygame.draw.rect(self.screen, WHITE,
|
||||
(panel_x + x * BLOCK_SIZE,
|
||||
200 + y * BLOCK_SIZE,
|
||||
BLOCK_SIZE, BLOCK_SIZE), 1)
|
||||
|
||||
# Draw score and level
|
||||
score_text = self.font_small.render(f'Score: {self.score}', True, WHITE)
|
||||
self.screen.blit(score_text, (panel_x, 300))
|
||||
|
||||
high_score_text = self.font_small.render(f'High: {self.high_score}', True, WHITE)
|
||||
self.screen.blit(high_score_text, (panel_x, 340))
|
||||
|
||||
level_text = self.font_small.render(f'Level: {self.level}', True, WHITE)
|
||||
self.screen.blit(level_text, (panel_x, 380))
|
||||
|
||||
lines_text = self.font_small.render(f'Lines: {self.lines_cleared}', True, WHITE)
|
||||
self.screen.blit(lines_text, (panel_x, 420))
|
||||
|
||||
if self.combo > 1:
|
||||
combo_text = self.font_small.render(f'Combo: x{self.combo}', True, WHITE)
|
||||
self.screen.blit(combo_text, (panel_x, 460))
|
||||
|
||||
# Draw particles
|
||||
self.particles = [p for p in self.particles if p.update()]
|
||||
for particle in self.particles:
|
||||
particle.draw(self.screen)
|
||||
|
||||
# Draw line clear flash effect
|
||||
if self.animation.flash_active and self.animation.lines_being_cleared:
|
||||
flash_alpha = int(255 * (1 - self.animation.flash_progress / FLASH_SPEED))
|
||||
if flash_alpha > 0:
|
||||
flash_surface = pygame.Surface((WINDOW_WIDTH, WINDOW_HEIGHT), pygame.SRCALPHA)
|
||||
for y in self.animation.lines_being_cleared:
|
||||
pygame.draw.rect(flash_surface, (255, 255, 255, flash_alpha),
|
||||
(0, y * BLOCK_SIZE, GRID_WIDTH * BLOCK_SIZE, BLOCK_SIZE))
|
||||
self.screen.blit(flash_surface, (0, 0))
|
||||
|
||||
# Draw game over or pause screen
|
||||
if self.game_over:
|
||||
self._draw_overlay("Game Over! Press R to restart")
|
||||
elif self.paused:
|
||||
self._draw_overlay("Paused")
|
||||
|
||||
pygame.display.flip()
|
||||
|
||||
def _draw_overlay(self, text: str):
|
||||
overlay = pygame.Surface((WINDOW_WIDTH, WINDOW_HEIGHT))
|
||||
overlay.set_alpha(128)
|
||||
overlay.fill(BLACK)
|
||||
self.screen.blit(overlay, (0, 0))
|
||||
|
||||
text_surface = self.font_big.render(text, True, WHITE)
|
||||
text_rect = text_surface.get_rect(center=(WINDOW_WIDTH//2, WINDOW_HEIGHT//2))
|
||||
self.screen.blit(text_surface, text_rect)
|
||||
|
||||
def reset(self):
|
||||
self.grid = [[None for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)]
|
||||
self.current_piece = None
|
||||
self.current_shape = None
|
||||
self.current_pos = None
|
||||
self.held_piece = None
|
||||
self.can_hold = True
|
||||
self.next_piece = self._get_random_piece()
|
||||
self.game_over = False
|
||||
self.score = 0
|
||||
self.level = 1
|
||||
self.lines_cleared = 0
|
||||
self.particles = []
|
||||
self.fall_speed = INITIAL_FALL_SPEED
|
||||
self.current_fall_speed = INITIAL_FALL_SPEED
|
||||
self.last_fall_time = time.time()
|
||||
self.lock_delay_time = 0
|
||||
self.lock_delay_active = False
|
||||
self.lock_reset_count = 0
|
||||
self.combo = 0
|
||||
self.animation = AnimationState()
|
||||
self.paused = False
|
||||
self.new_piece()
|
||||
|
||||
def update_animations(self, dt: float):
|
||||
# Update move animation
|
||||
if self.animation.move_progress < MOVE_ANIMATION_SPEED:
|
||||
self.animation.move_progress += dt
|
||||
|
||||
# Update rotation animation
|
||||
if self.animation.rotation_progress < ROTATION_ANIMATION_SPEED:
|
||||
self.animation.rotation_progress += dt
|
||||
|
||||
# Update line clear animation
|
||||
if self.animation.line_clear_progress < LINE_CLEAR_ANIMATION_TIME:
|
||||
self.animation.line_clear_progress += dt
|
||||
|
||||
# Update flash animation
|
||||
if self.animation.flash_active:
|
||||
self.animation.flash_progress += dt
|
||||
if self.animation.flash_progress >= FLASH_SPEED:
|
||||
self.animation.flash_active = False
|
||||
self.animation.flash_progress = 0
|
||||
|
||||
def run(self):
|
||||
self.new_piece()
|
||||
last_time = time.time()
|
||||
|
||||
while True:
|
||||
current_time = time.time()
|
||||
dt = current_time - last_time
|
||||
last_time = current_time
|
||||
|
||||
self.clock.tick(60)
|
||||
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
pygame.quit()
|
||||
return
|
||||
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_ESCAPE:
|
||||
pygame.quit()
|
||||
return
|
||||
|
||||
if self.game_over:
|
||||
if event.key == pygame.K_r:
|
||||
self.reset()
|
||||
continue
|
||||
|
||||
if event.key == pygame.K_p:
|
||||
self.paused = not self.paused
|
||||
continue
|
||||
|
||||
if self.paused:
|
||||
continue
|
||||
|
||||
if event.key == pygame.K_LEFT:
|
||||
self.move(-1, 0)
|
||||
elif event.key == pygame.K_RIGHT:
|
||||
self.move(1, 0)
|
||||
elif event.key == pygame.K_DOWN:
|
||||
self.current_fall_speed = SOFT_DROP_SPEED
|
||||
elif event.key == pygame.K_UP:
|
||||
self.rotate_piece()
|
||||
elif event.key == pygame.K_z:
|
||||
self.rotate_piece(False)
|
||||
elif event.key == pygame.K_SPACE:
|
||||
self.hard_drop()
|
||||
elif event.key == pygame.K_c:
|
||||
self.hold_piece()
|
||||
|
||||
elif event.type == pygame.KEYUP:
|
||||
if event.key == pygame.K_DOWN:
|
||||
self.current_fall_speed = self.fall_speed
|
||||
|
||||
if not self.game_over and not self.paused:
|
||||
# Update animations
|
||||
self.update_animations(dt)
|
||||
|
||||
# Handle automatic falling
|
||||
if current_time - self.last_fall_time > self.current_fall_speed:
|
||||
self.move(0, 1) # Move down one grid
|
||||
self.last_fall_time = current_time
|
||||
|
||||
self.draw()
|
||||
|
||||
if __name__ == '__main__':
|
||||
game = Tetris()
|
||||
game.run()
|
||||
2
WareHouse/tetris_THUNLPDemo_2024/requirements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
pygame==2.5.2
|
||||
numpy==1.24.3
|
||||
BIN
misc/car_game.png
Normal file
|
After Width: | Height: | Size: 225 KiB |
BIN
misc/pvz.png
Normal file
|
After Width: | Height: | Size: 435 KiB |
BIN
misc/snake_game.png
Normal file
|
After Width: | Height: | Size: 614 KiB |
BIN
misc/tetris.png
Normal file
|
After Width: | Height: | Size: 292 KiB |
BIN
misc/tsinghua_bamboo_website.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |