1+ <!DOCTYPE html>
2+ < html lang ="en ">
3+ < head >
4+ < meta charset ="UTF-8 ">
5+ < meta name ="viewport " content ="width=device-width, initial-scale=1.0 ">
6+ < title > Vector Clicker</ title >
7+ < script src ="webxdc.js "> </ script >
8+ < style >
9+ * {
10+ margin : 0 ;
11+ padding : 0 ;
12+ box-sizing : border-box;
13+ }
14+
15+ body {
16+ font-family : -apple-system, BlinkMacSystemFont, 'Segoe UI' , Roboto, Oxygen, Ubuntu, sans-serif;
17+ background : linear-gradient (135deg , # 667eea 0% , # 764ba2 100% );
18+ min-height : 100vh ;
19+ display : flex;
20+ flex-direction : column;
21+ align-items : center;
22+ justify-content : center;
23+ padding : 20px ;
24+ color : white;
25+ }
26+
27+ h1 {
28+ font-size : 2rem ;
29+ margin-bottom : 10px ;
30+ text-shadow : 2px 2px 4px rgba (0 , 0 , 0 , 0.3 );
31+ }
32+
33+ .player-info {
34+ background : rgba (255 , 255 , 255 , 0.2 );
35+ padding : 10px 20px ;
36+ border-radius : 20px ;
37+ margin-bottom : 20px ;
38+ font-size : 0.9rem ;
39+ }
40+
41+ .score-display {
42+ font-size : 3rem ;
43+ font-weight : bold;
44+ margin : 20px 0 ;
45+ text-shadow : 3px 3px 6px rgba (0 , 0 , 0 , 0.3 );
46+ }
47+
48+ .click-button {
49+ width : 200px ;
50+ height : 200px ;
51+ border-radius : 50% ;
52+ border : none;
53+ background : linear-gradient (145deg , # ff6b6b, # ee5a5a );
54+ box-shadow :
55+ 0 10px 30px rgba (0 , 0 , 0 , 0.3 ),
56+ inset 0 -5px 20px rgba (0 , 0 , 0 , 0.2 ),
57+ inset 0 5px 20px rgba (255 , 255 , 255 , 0.2 );
58+ cursor : pointer;
59+ font-size : 4rem ;
60+ color : white;
61+ transition : transform 0.1s , box-shadow 0.1s ;
62+ user-select : none;
63+ }
64+
65+ .click-button : hover {
66+ transform : scale (1.05 );
67+ }
68+
69+ .click-button : active {
70+ transform : scale (0.95 );
71+ box-shadow :
72+ 0 5px 15px rgba (0 , 0 , 0 , 0.3 ),
73+ inset 0 -2px 10px rgba (0 , 0 , 0 , 0.2 ),
74+ inset 0 2px 10px rgba (255 , 255 , 255 , 0.2 );
75+ }
76+
77+ .click-effect {
78+ position : fixed;
79+ pointer-events : none;
80+ font-size : 1.5rem ;
81+ font-weight : bold;
82+ color : # ffd700 ;
83+ text-shadow : 1px 1px 2px rgba (0 , 0 , 0 , 0.5 );
84+ animation : floatUp 1s ease-out forwards;
85+ }
86+
87+ @keyframes floatUp {
88+ 0% {
89+ opacity : 1 ;
90+ transform : translateY (0 ) scale (1 );
91+ }
92+ 100% {
93+ opacity : 0 ;
94+ transform : translateY (-100px ) scale (1.5 );
95+ }
96+ }
97+
98+ .stats {
99+ margin-top : 30px ;
100+ font-size : 0.9rem ;
101+ opacity : 0.8 ;
102+ }
103+ </ style >
104+ </ head >
105+ < body >
106+ < h1 > 🎮 Vector Clicker</ h1 >
107+ < div class ="player-info " id ="playerInfo "> Loading...</ div >
108+
109+ < div class ="score-display " id ="score "> 0</ div >
110+
111+ < button class ="click-button " id ="clickBtn " onclick ="handleClick(event) ">
112+ 👆
113+ </ button >
114+
115+ < div class ="stats " id ="stats ">
116+ Clicks per second: < span id ="cps "> 0</ span >
117+ </ div >
118+
119+ < script >
120+ // Game state
121+ let myScore = 0 ;
122+ let clickTimes = [ ] ;
123+ let myAddr = 'unknown' ;
124+ let myName = 'Player' ;
125+
126+ // Initialize the game
127+ function init ( ) {
128+ // Get player info from webxdc
129+ if ( window . webxdc ) {
130+ myAddr = window . webxdc . selfAddr || 'unknown' ;
131+ myName = window . webxdc . selfName || 'Player' ;
132+
133+ document . getElementById ( 'playerInfo' ) . textContent =
134+ `Playing as: ${ myName } ` ;
135+
136+ // Set up update listener
137+ window . webxdc . setUpdateListener ( function ( update ) {
138+ handleUpdate ( update ) ;
139+ } , 0 ) ;
140+ } else {
141+ document . getElementById ( 'playerInfo' ) . textContent =
142+ 'Demo mode (webxdc not available)' ;
143+ }
144+
145+ // Update CPS every second
146+ setInterval ( updateCPS , 1000 ) ;
147+ }
148+
149+ // Handle click on the button
150+ function handleClick ( event ) {
151+ myScore ++ ;
152+ updateScoreDisplay ( ) ;
153+ createClickEffect ( event ) ;
154+
155+ // Track click time for CPS calculation
156+ clickTimes . push ( Date . now ( ) ) ;
157+
158+ // Send update to other players
159+ if ( window . webxdc ) {
160+ window . webxdc . sendUpdate ( {
161+ payload : {
162+ type : 'score' ,
163+ addr : myAddr ,
164+ name : myName ,
165+ score : myScore
166+ }
167+ } , `${ myName } clicked! Score: ${ myScore } ` ) ;
168+ }
169+ }
170+
171+ // Handle updates from other players
172+ function handleUpdate ( update ) {
173+ if ( update . payload && update . payload . type === 'score' ) {
174+ const { addr, name, score } = update . payload ;
175+
176+ // If it's our own update coming back, sync the score
177+ if ( addr === myAddr && score > myScore ) {
178+ myScore = score ;
179+ updateScoreDisplay ( ) ;
180+ }
181+ }
182+ }
183+
184+ // Update the score display
185+ function updateScoreDisplay ( ) {
186+ document . getElementById ( 'score' ) . textContent = myScore . toLocaleString ( ) ;
187+ }
188+
189+ // Create floating +1 effect
190+ function createClickEffect ( event ) {
191+ const effect = document . createElement ( 'div' ) ;
192+ effect . className = 'click-effect' ;
193+ effect . textContent = '+1' ;
194+ effect . style . left = ( event . clientX - 15 ) + 'px' ;
195+ effect . style . top = ( event . clientY - 15 ) + 'px' ;
196+ document . body . appendChild ( effect ) ;
197+
198+ setTimeout ( ( ) => effect . remove ( ) , 1000 ) ;
199+ }
200+
201+ // Calculate and display clicks per second
202+ function updateCPS ( ) {
203+ const now = Date . now ( ) ;
204+ const oneSecondAgo = now - 1000 ;
205+
206+ // Filter to only clicks in the last second
207+ clickTimes = clickTimes . filter ( t => t > oneSecondAgo ) ;
208+
209+ document . getElementById ( 'cps' ) . textContent = clickTimes . length ;
210+ }
211+
212+ // Start the game
213+ init ( ) ;
214+ </ script >
215+ </ body >
216+ </ html >
0 commit comments