characterprofile.php

main 866 lines · 31.8 KB Raw
Alex Alex Commit Initial commit 01/10/2026 09:20
1<?php require_once 'engine/init.php'; theme_open();
2
3if ($config['log_ip']) {
4 znote_visitor_insert_detailed_data(4);
5}
6
7if (isset($_GET['name']) === true && empty($_GET['name']) === false) {
8 $name = getValue($_GET['name'] ?? null);
9 $user_id = user_character_exist($name);
10
11 if ($user_id !== false) {
12 $loadOutfits = $config['show_outfits']['characterprofile'];
13
14 if (!$loadOutfits) {
15 $profile_data = user_character_data($user_id, 'account_id', 'name', 'level', 'group_id', 'vocation', 'health', 'healthmax', 'experience', 'mana', 'manamax', 'sex', 'lastlogin', 'town_id');
16 } else { // Load outfits
17 if ($config['client'] < 780) {
18 $profile_data = user_character_data($user_id, 'account_id', 'name', 'level', 'group_id', 'vocation', 'health', 'healthmax', 'experience', 'mana', 'manamax', 'sex', 'lastlogin', 'town_id', 'lookbody', 'lookfeet', 'lookhead', 'looklegs', 'looktype');
19 } else {
20 $profile_data = user_character_data($user_id, 'account_id', 'name', 'level', 'group_id', 'vocation', 'health', 'healthmax', 'experience', 'mana', 'manamax', 'sex', 'lastlogin', 'town_id', 'lookbody', 'lookfeet', 'lookhead', 'looklegs', 'looktype', 'lookaddons');
21 }
22 }
23 $profile_data['online'] = user_is_online_10($user_id);
24
25 if ($config['Ach']) {
26 $user_id = (int) $user_id;
27 $achievementPoints = db()->fetchOne("SELECT SUM(`value`) AS `sum` FROM `player_storage` WHERE `key` LIKE '30___' AND `player_id` = ? LIMIT 1", [$user_id]);
28 }
29
30 $profile_znote_data = user_znote_character_data($user_id, 'created', 'hide_char', 'comment');
31 $guild_exist = false;
32 if (get_character_guild_rank($user_id) > 0) {
33 $guild_exist = true;
34 $guild = get_player_guild_data($user_id);
35 $guild_name = get_guild_name($guild['guild_id']);
36 }
37
38 $position = '';
39 if ($profile_data['group_id'] > 1) {
40 $position_data = db()->fetchOne("
41 SELECT `a`.`type`
42 FROM `players` AS `p`
43 INNER JOIN `accounts` AS `a`
44 ON `p`.`account_id` = `a`.`id`
45 WHERE
46 `a`.`type` > 1
47 AND `p`.`id` = ?
48 ", [$user_id]);
49 $position_type = ($position_data !== false) ? $position_data['type'] : null;
50 $position = (isset($config['ingame_positions'][$position_type])) ? $config['ingame_positions'][$position_type] : t('common.unknown');
51 }
52
53 $deletion_time = db()->fetchOne("SELECT `time` FROM `znote_deleted_characters` WHERE `character_name` = ? AND `done` = '0' LIMIT 1;", [$name]);
54 $houses = db()->fetchAll("
55 SELECT `id`, `owner`, `name`, `town_id` AS `town_id`
56 FROM `houses`
57 WHERE `owner` = ?;
58 ", [$user_id]);
59 ?>
60
61 <!-- PROFILE MARKUP HERE-->
62 <?php
63 // Item image server
64 $imageServer = $config['shop']['imageServer'];
65 $imageType = $config['shop']['imageType'];
66 $PEQ = db()->fetchAll("
67 SELECT
68 `player_id`,
69 `pid`,
70 `itemtype`,
71 `count`
72 FROM `player_items`
73 WHERE `player_id` = ?
74 AND `pid` < '11'
75 ", [$user_id]);
76
77 $soulStamina = " `soul`, `stamina`,";
78 if ($config['client'] < 780) {
79 $soulStamina = " 0 AS `soul`, 0 AS `stamina`,";
80 }
81
82 $player_query = "
83 SELECT
84 `health`, `healthmax`,
85 `mana`, `manamax`,
86 `cap`,
87 `experience`, `level`,
88 {$soulStamina}
89 `maglevel`,
90 `skill_fist`,
91 `skill_club`,
92 `skill_sword`,
93 `skill_axe`,
94 `skill_dist`,
95 `skill_shielding`,
96 `skill_fishing`
97 FROM `players`
98 WHERE `id` = ?
99 LIMIT 1;
100 ";
101 $playerstats = db()->fetchOne($player_query, [$user_id]);
102
103 $player_experience_raw = (int)$playerstats['experience'];
104 $profile_flag = '';
105 $flags = $config['country_flags'];
106 if ($flags['enabled'] && $flags['characterprofile']) {
107 $flag_account = user_znote_account_data($profile_data['account_id'], 'flag');
108 if ($flag_account !== false && strlen($flag_account['flag']) > 0) {
109 $profile_flag = (string)$flag_account['flag'];
110 }
111 }
112 $profile_houses = array();
113 if ($houses !== false) {
114 foreach ($houses as $h) {
115 $profile_houses[] = $h['name'] . ', ' . ($config['towns'][$h['town_id']] ?? $h['town_id']);
116 }
117 }
118 $profile_account = user_data((int)$profile_data['account_id'], 'premium_ends_at');
119 $premium_until = ($profile_account !== false) ? (int)($profile_account['premium_ends_at'] ?? 0) : 0;
120 $account_status = ((bool)($config['freePremium'] ?? false) || $premium_until > time()) ? t('char.premium_active') : t('char.premium_inactive');
121 $profile_lookaddons = (int)($profile_data['lookaddons'] ?? 0);
122 $current_outfit_src = $loadOutfits
123 ? $config['show_outfits']['imageServer'] . '?id=' . (int)$profile_data['looktype'] . '&addons=' . $profile_lookaddons . '&head=' . (int)$profile_data['lookhead'] . '&body=' . (int)$profile_data['lookbody'] . '&legs=' . (int)$profile_data['looklegs'] . '&feet=' . (int)$profile_data['lookfeet']
124 : '';
125 $health_percent = ((int)$playerstats['healthmax'] > 0) ? min(100, max(0, round(((int)$playerstats['health'] / (int)$playerstats['healthmax']) * 100, 2))) : 100;
126 $mana_percent = ((int)$playerstats['manamax'] > 0) ? min(100, max(0, round(((int)$playerstats['mana'] / (int)$playerstats['manamax']) * 100, 2))) : 100;
127 $experience_raw = $player_experience_raw;
128 $level_raw = (int)$playerstats['level'];
129 $level_start_exp = (int)level_to_experience($level_raw);
130 $level_next_exp = (int)level_to_experience($level_raw + 1);
131 $level_exp_span = max(1, $level_next_exp - $level_start_exp);
132 $level_exp_done = min($level_exp_span, max(0, $experience_raw - $level_start_exp));
133 $level_percent = round(($level_exp_done / $level_exp_span) * 100, 2);
134 $level_needed = max(0, $level_next_exp - $experience_raw);
135 $detail_skill_rows = array(
136 array('label' => t('common.level'), 'value' => $playerstats['level']),
137 array('label' => t('char.magic_level'), 'value' => $playerstats['maglevel']),
138 array('label' => t('skill.fist'), 'value' => $playerstats['skill_fist']),
139 array('label' => t('skill.club'), 'value' => $playerstats['skill_club']),
140 array('label' => t('skill.sword'), 'value' => $playerstats['skill_sword']),
141 array('label' => t('skill.axe'), 'value' => $playerstats['skill_axe']),
142 array('label' => t('skill.distance'), 'value' => $playerstats['skill_dist']),
143 array('label' => t('skill.shielding'), 'value' => $playerstats['skill_shielding']),
144 array('label' => t('skill.fishing'), 'value' => $playerstats['skill_fishing']),
145 );
146 ?>
147 <?php if (function_exists('tco_characterprofile_open')) { tco_characterprofile_open(); } ?>
148 <div id="characterProfileTable" class="cp-profile-shell">
149 <div class="cp-profile-rebuild">
150 <?php if ($deletion_time !== false): ?>
151 <div class="cp-alert"><?= t('char.flagged_delete2') ?> <?php echo htmlspecialchars((string)$deletion_time['time'], ENT_QUOTES, 'UTF-8'); ?>.</div>
152 <?php endif; ?>
153
154 <div class="cp-profile-top">
155 <section class="cp-panel cp-info-panel">
156 <header class="cp-panel-head">
157 <h2><?= t('char.info_title') ?></h2>
158 </header>
159 <div class="cp-info-list">
160 <div class="cp-info-row">
161 <span><?= t('common.name') ?></span>
162 <strong><?php echo htmlspecialchars($profile_data['name'], ENT_QUOTES, 'UTF-8'); ?> <em class="cp-status cp-status--<?php echo $profile_data['online'] ? 'online' : 'offline'; ?>"><?php echo $profile_data['online'] ? t('common.status_on') : t('common.status_off'); ?></em></strong>
163 </div>
164 <?php if ($position !== ''): ?>
165 <div class="cp-info-row">
166 <span><?= t('char.position') ?></span>
167 <strong><?php echo htmlspecialchars($position, ENT_QUOTES, 'UTF-8'); ?></strong>
168 </div>
169 <?php endif; ?>
170 <?php if ($profile_flag !== ''): ?>
171 <div class="cp-info-row">
172 <span><?= t('common.country') ?></span>
173 <strong><?php echo strtoupper(htmlspecialchars($profile_flag, ENT_QUOTES, 'UTF-8')); ?> <img class="cp-flag" src="<?php echo htmlspecialchars($flags['server'] . '/' . $profile_flag . '.png', ENT_QUOTES, 'UTF-8'); ?>" alt="<?php echo htmlspecialchars($profile_flag, ENT_QUOTES, 'UTF-8'); ?>"></strong>
174 </div>
175 <?php endif; ?>
176 <div class="cp-info-row">
177 <span><?= t('common.sex') ?></span>
178 <strong><?php echo ($profile_data['sex'] == 1) ? t('common.male') : t('common.female'); ?></strong>
179 </div>
180 <div class="cp-info-row">
181 <span><?= t('common.vocation') ?></span>
182 <strong><?php echo htmlspecialchars(vocation_id_to_name($profile_data['vocation']), ENT_QUOTES, 'UTF-8'); ?></strong>
183 </div>
184 <div class="cp-info-row">
185 <span><?= t('common.level') ?></span>
186 <strong><?php echo (int)$profile_data['level']; ?></strong>
187 </div>
188 <?php if ($config['Ach']): ?>
189 <div class="cp-info-row">
190 <span><?= t('char.ach_points') ?></span>
191 <strong><?php echo (int)($achievementPoints['sum'] ?? 0); ?></strong>
192 </div>
193 <?php endif; ?>
194 <div class="cp-info-row">
195 <span><?= t('char.residence') ?></span>
196 <strong><?php echo htmlspecialchars($config['towns'][$profile_data['town_id']] ?? (string)$profile_data['town_id'], ENT_QUOTES, 'UTF-8'); ?></strong>
197 </div>
198 <?php if (!empty($profile_houses)): ?>
199 <div class="cp-info-row">
200 <span><?= t('char.house_label') ?></span>
201 <strong><?php echo htmlspecialchars(implode(', ', $profile_houses), ENT_QUOTES, 'UTF-8'); ?></strong>
202 </div>
203 <?php endif; ?>
204 <?php if ($guild_exist): ?>
205 <div class="cp-info-row">
206 <span><?= t('char.guild_label') ?></span>
207 <strong><?php echo htmlspecialchars($guild['rank_name'], ENT_QUOTES, 'UTF-8'); ?> <?= t('char.guild_of') ?> <a href="guilds.php?name=<?php echo urlencode($guild_name); ?>"><?php echo htmlspecialchars($guild_name, ENT_QUOTES, 'UTF-8'); ?></a></strong>
208 </div>
209 <?php endif; ?>
210 <div class="cp-info-row">
211 <span><?= t('char.last_login') ?></span>
212 <strong><?php echo ($profile_data['lastlogin'] != 0) ? htmlspecialchars(getClock($profile_data['lastlogin'], true, true), ENT_QUOTES, 'UTF-8') : t('char.never'); ?></strong>
213 </div>
214 <div class="cp-info-row">
215 <span><?= t('char.created') ?></span>
216 <strong><?php echo htmlspecialchars(getClock($profile_znote_data['created'], true), ENT_QUOTES, 'UTF-8'); ?></strong>
217 </div>
218 <div class="cp-info-row">
219 <span><?= t('char.account_status') ?></span>
220 <strong><?php echo htmlspecialchars($account_status, ENT_QUOTES, 'UTF-8'); ?></strong>
221 </div>
222 </div>
223 </section>
224
225 <?php if ($config['EQ_shower']['equipment']): ?>
226 <section class="cp-panel cp-equipment-panel">
227 <header class="cp-panel-head">
228 <h2><?= t('char.equipment') ?></h2>
229 </header>
230 <div class="cp-equipment-stage">
231 <?php if ($loadOutfits && $current_outfit_src !== ''): ?>
232 <div class="cp-current-outfit">
233 <img src="<?php echo htmlspecialchars($current_outfit_src, ENT_QUOTES, 'UTF-8'); ?>" alt="<?php echo htmlspecialchars($profile_data['name'], ENT_QUOTES, 'UTF-8'); ?>">
234 </div>
235 <?php endif; ?>
236 <div id="cp_equipment_board">
237 <img class="cp-equipment-bg" src="/engine/img/outfit.png" alt="">
238 <?php if ($PEQ !== false && !empty($PEQ)): foreach($PEQ as $item): ?>
239 <div class="itm itm-<?php echo (int)$item['pid']; ?>">
240 <img class="cp-slot-empty" src="/engine/img/empty.png" alt="">
241 <img class="cp-slot-item" src="<?php echo htmlspecialchars(znote_item_image_url((int)$item["itemtype"]), ENT_QUOTES, "UTF-8"); ?>" alt="">
242 </div>
243 <?php endforeach; endif; ?>
244 </div>
245 <div class="cp-equipment-meta">
246 <span><?= t('skill.soul') ?> <strong><?php echo (int)$playerstats['soul']; ?></strong></span>
247 <span><?= t('skill.cap') ?> <strong><?php echo number_format((int)$playerstats['cap'],0,'',','); ?></strong></span>
248 </div>
249 </div>
250 </section>
251 <?php endif; ?>
252 </div>
253
254 <section class="cp-panel cp-details-panel">
255 <header class="cp-panel-head">
256 <h2><?= t('char.details') ?></h2>
257 </header>
258 <div class="cp-bars">
259 <div class="cp-bar-block">
260 <div class="cp-bar-label"><span><?= t('skill.health') ?></span><strong><?php echo number_format((int)$playerstats['health'],0,'',','); ?> / <?php echo number_format((int)$playerstats['healthmax'],0,'',','); ?></strong></div>
261 <div class="cp-bar-track"><i class="cp-bar-fill cp-bar-fill--health" style="width: <?php echo $health_percent; ?>%;"></i></div>
262 </div>
263 <div class="cp-bar-block">
264 <div class="cp-bar-label"><span><?= t('skill.mana') ?></span><strong><?php echo number_format((int)$playerstats['mana'],0,'',','); ?> / <?php echo number_format((int)$playerstats['manamax'],0,'',','); ?></strong></div>
265 <div class="cp-bar-track"><i class="cp-bar-fill cp-bar-fill--mana" style="width: <?php echo $mana_percent; ?>%;"></i></div>
266 </div>
267 <div class="cp-bar-block">
268 <div class="cp-bar-label"><span><?= t('skill.experience') ?> - <?= t('common.level') ?> <?php echo (int)$playerstats['level']; ?></span><strong><?php echo $level_percent; ?>% to <?php echo (int)$playerstats['level'] + 1; ?></strong></div>
269 <div class="cp-bar-track"><i class="cp-bar-fill cp-bar-fill--experience" style="width: <?php echo $level_percent; ?>%;"></i></div>
270 <div class="cp-exp-note"><?= t('char.need_exp', ['amount' => '<strong>' . number_format($level_needed,0,'',',') . '</strong>', 'level' => (int)$playerstats['level'] + 1]) ?></div>
271 </div>
272 </div>
273 <?php if ($config['EQ_shower']['skills']): ?>
274 <div class="cp-skill-strip">
275 <?php foreach ($detail_skill_rows as $skill_row): ?>
276 <div class="cp-skill-tile">
277 <span><?php echo htmlspecialchars((string)$skill_row['label'], ENT_QUOTES, 'UTF-8'); ?></span>
278 <strong><?php echo htmlspecialchars((string)$skill_row['value'], ENT_QUOTES, 'UTF-8'); ?></strong>
279 </div>
280 <?php endforeach; ?>
281 </div>
282 <?php endif; ?>
283 </section>
284 </div>
285
286 <!-- Inventory style positioning -->
287 <style type="text/css">
288 #characterProfileTable {
289 background: transparent !important;
290 border: 0 !important;
291 box-shadow: none !important;
292 overflow: visible !important;
293 display: block;
294 width: 100%;
295 max-width: 100%;
296 margin: 0 0 16px;
297 }
298 .cp-profile-rebuild {
299 --cp-border: var(--s-border, rgba(255, 255, 255, .16));
300 --cp-border-strong: var(--s-border2, rgba(255, 255, 255, .28));
301 --cp-text: var(--s-text, currentColor);
302 --cp-muted: var(--s-muted, currentColor);
303 --cp-accent: var(--s-gold, #f0c028);
304 --cp-panel-bg: var(--profile-panel-bg, transparent);
305 color: var(--cp-text);
306 }
307 .cp-profile-top {
308 display: grid;
309 grid-template-columns: minmax(280px, 1fr) minmax(300px, 1fr);
310 gap: 16px;
311 align-items: stretch;
312 }
313 .cp-panel {
314 position: relative;
315 min-width: 0;
316 border: 1px solid var(--cp-border);
317 border-radius: 10px;
318 background: var(--cp-panel-bg);
319 overflow: hidden;
320 }
321 .cp-panel-head {
322 display: flex;
323 align-items: center;
324 min-height: 46px;
325 padding: 0 16px;
326 border-bottom: 1px solid var(--cp-border);
327 background: transparent;
328 }
329 .cp-panel-head h2 {
330 margin: 0;
331 color: inherit;
332 font-size: 15px;
333 font-weight: 800;
334 letter-spacing: 0;
335 }
336 .cp-info-list {
337 padding: 12px 16px 16px;
338 }
339 .cp-info-row {
340 display: grid;
341 grid-template-columns: minmax(120px, 42%) 1fr;
342 gap: 14px;
343 align-items: center;
344 min-height: 39px;
345 border-bottom: 1px solid var(--cp-border);
346 }
347 .cp-info-row:last-child {
348 border-bottom: 0;
349 }
350 .cp-info-row > span {
351 color: var(--cp-muted);
352 font-weight: 800;
353 }
354 .cp-info-row > strong {
355 display: flex;
356 align-items: center;
357 gap: 6px;
358 min-width: 0;
359 color: inherit;
360 font-weight: 700;
361 }
362 .cp-flag {
363 display: inline-block;
364 width: auto;
365 height: 11px;
366 max-width: none;
367 }
368 .cp-status {
369 color: #ff3b3b;
370 font-size: 9px;
371 font-style: normal;
372 font-weight: 900;
373 }
374 .cp-status--online {
375 color: #35d27f;
376 }
377 .cp-alert {
378 margin-bottom: 12px;
379 padding: 10px 14px;
380 border: 1px solid var(--cp-border-strong);
381 border-left: 3px solid #ff3b3b;
382 border-radius: 8px;
383 }
384 .cp-equipment-stage {
385 min-height: 390px;
386 padding: 12px 16px 28px;
387 display: flex;
388 flex-direction: column;
389 align-items: center;
390 justify-content: center;
391 }
392 .cp-current-outfit {
393 width: 100%;
394 height: 88px;
395 display: flex;
396 align-items: center;
397 justify-content: center;
398 margin-bottom: 24px;
399 position: relative;
400 z-index: 2;
401 }
402 .cp-current-outfit img {
403 width: 96px;
404 height: 96px;
405 max-width: 96px;
406 max-height: 96px;
407 object-fit: contain;
408 object-position: center;
409 transform: translateX(-22px);
410 filter: drop-shadow(0 8px 10px rgba(0, 0, 0, .42));
411 }
412 #cp_equipment_board {
413 --cp-equipped-slot-width: 50px;
414 --cp-equipped-slot-height: 51px;
415 --cp-equipped-item-size: 48px;
416 position: relative;
417 width: min(168px, 100%);
418 aspect-ratio: 3 / 4;
419 }
420 #cp_equipment_board .cp-equipment-bg {
421 width: 100%;
422 height: 100%;
423 object-fit: contain;
424 max-width: none;
425 }
426 #cp_equipment_board .itm {
427 position: absolute;
428 width: var(--cp-equipped-slot-width);
429 height: var(--cp-equipped-slot-height);
430 display: flex;
431 align-items: center;
432 justify-content: center;
433 transform: translate(-50%, -50%);
434 }
435 #cp_equipment_board .itm img {
436 position: absolute !important;
437 object-fit: contain;
438 }
439 #cp_equipment_board .cp-slot-empty {
440 left: 50%;
441 top: 50%;
442 z-index: 1;
443 width: var(--cp-equipped-slot-width);
444 height: var(--cp-equipped-slot-height);
445 max-width: var(--cp-equipped-slot-width);
446 max-height: var(--cp-equipped-slot-height);
447 transform: translate(-50%, -50%);
448 }
449 #cp_equipment_board .cp-slot-item {
450 left: 50%;
451 top: 50%;
452 z-index: 2;
453 width: var(--cp-equipped-item-size);
454 height: var(--cp-equipped-item-size);
455 max-width: var(--cp-equipped-item-size);
456 max-height: var(--cp-equipped-item-size);
457 transform: translate(-50%, -50%);
458 transition: transform .16s ease, filter .16s ease;
459 }
460 #cp_equipment_board .itm:hover .cp-slot-item {
461 transform: translate(-50%, calc(-50% - 4px));
462 filter: drop-shadow(0 7px 9px rgba(0, 0, 0, .5));
463 }
464 #cp_equipment_board .itm-1 { left: 50%; top: 18.04%; }
465 #cp_equipment_board .itm-2 { left: 20.17%; top: 18.04%; }
466 #cp_equipment_board .itm-3 { left: 79.67%; top: 18.04%; }
467 #cp_equipment_board .itm-4 { left: 50%; top: 40.02%; }
468 #cp_equipment_board .itm-5 { left: 79.67%; top: 40.02%; }
469 #cp_equipment_board .itm-6 { left: 20.17%; top: 40.02%; }
470 #cp_equipment_board .itm-7 { left: 50%; top: 61.56%; }
471 #cp_equipment_board .itm-8 { left: 50%; top: 83.25%; }
472 #cp_equipment_board .itm-9 { left: 20.17%; top: 61.56%; }
473 #cp_equipment_board .itm-10 { left: 79.67%; top: 61.56%; }
474 .cp-equipment-meta {
475 display: flex;
476 justify-content: center;
477 gap: 28px;
478 margin-top: 24px;
479 color: var(--cp-muted);
480 font-weight: 700;
481 }
482 .cp-equipment-meta strong {
483 color: inherit;
484 }
485 .cp-details-panel {
486 margin-top: 10px;
487 }
488 .cp-bars {
489 padding: 14px 16px 4px;
490 }
491 .cp-bar-block {
492 margin-bottom: 14px;
493 }
494 .cp-bar-label {
495 display: flex;
496 justify-content: space-between;
497 gap: 16px;
498 margin-bottom: 7px;
499 font-weight: 800;
500 }
501 .cp-bar-label strong {
502 color: inherit;
503 }
504 .cp-exp-note strong {
505 color: var(--cp-accent);
506 }
507 .cp-bar-track {
508 height: 12px;
509 border: 1px solid var(--cp-border);
510 border-radius: 999px;
511 background: var(--cp-track-bg, transparent);
512 overflow: hidden;
513 }
514 .cp-bar-fill {
515 display: block;
516 height: 100%;
517 border-radius: inherit;
518 }
519 .cp-bar-fill--health { background: #bd2b2b; }
520 .cp-bar-fill--mana { background: #2f84c9; }
521 .cp-bar-fill--experience { background: var(--cp-accent); }
522 .cp-exp-note {
523 margin-top: 6px;
524 color: var(--cp-muted);
525 font-size: 12px;
526 }
527 .cp-skill-strip {
528 display: grid;
529 grid-template-columns: repeat(auto-fit, minmax(72px, 1fr));
530 gap: 8px;
531 padding: 0 16px 16px;
532 }
533 .cp-skill-tile {
534 min-height: 84px;
535 padding: 10px 8px;
536 border: 1px solid var(--cp-border);
537 border-top: 2px solid var(--cp-border-strong);
538 border-radius: 8px;
539 background: transparent;
540 text-align: center;
541 overflow: hidden;
542 }
543 .cp-skill-tile span {
544 display: block;
545 min-height: 28px;
546 color: var(--cp-muted);
547 font-size: 10px;
548 font-weight: 800;
549 line-height: 1.25;
550 text-transform: uppercase;
551 }
552 .cp-skill-tile strong {
553 display: block;
554 margin-top: 4px;
555 color: inherit;
556 font-size: 18px;
557 font-weight: 900;
558 }
559 @media (max-width: 980px) {
560 .cp-profile-top {
561 grid-template-columns: 1fr;
562 }
563 }
564 @media (max-width: 520px) {
565 .cp-info-row {
566 grid-template-columns: 1fr;
567 gap: 2px;
568 padding: 8px 0;
569 }
570 .cp-bar-label {
571 flex-direction: column;
572 gap: 2px;
573 }
574 .cp-equipment-stage {
575 padding-left: 10px;
576 padding-right: 10px;
577 }
578 }
579
580 </style>
581 </div>
582 <!-- End profile -->
583
584 <!-- Player Comment -->
585 <?php if (!empty($profile_znote_data['comment'])): ?>
586 <table class="comment">
587 <thead>
588 <tr class="yellow">
589 <td><font class="profile_font" name="profile_font_comment"><?= t('char.comment') ?></font></td>
590 </tr>
591 </thead>
592 <tbody>
593 <tr>
594 <td><?php echo preg_replace('/\v+|\\\r\\\n/','<br/>',$profile_znote_data['comment']); ?></td>
595 </tr>
596 </tbody>
597 </table>
598 <?php endif; ?>
599
600 <!-- Achievements start -->
601 <?php if ($config['Ach']):
602 $achievements = db()->fetchAll("
603 SELECT `player_id`, `value`, `key`
604 FROM `player_storage`
605 WHERE `player_id` = ?
606 AND `key` LIKE '30___';
607 ", [$user_id]);
608 $c_achs = $config['achievements'];
609 $toggle = array(
610 'show' => '<a href="#show">' . t('common.show') . '</a>',
611 'hide' => '<a href="#hide">' . t('common.hide') . '</a>'
612 );
613 if ($achievements !== false): ?>
614 <h3><?= t('char.achievements') ?> <label id="ac_label_hide" for="ac_toggle_hide"><?php echo $toggle['show']; ?></label></h3>
615 <!-- <div id="accordion">
616 <h3><?= t('char.toggle_ach') ?></h3>
617 <div>
618
619 </div>
620 </div><br> -->
621 <input type="checkbox" id="ac_toggle_hide" name="ac_toggle_hide">
622 <table class="achievements">
623 <thead>
624 <tr>
625 <th><?= t('common.name') ?></th>
626 <th><?= t('common.description') ?></th>
627 <th><?= t('common.points') ?></th>
628 </tr>
629 </thead>
630 <tbody>
631 <?php foreach($achievements as $a): ?>
632 <tr>
633 <td><?php echo $c_achs[$a['key']][0]; ?></td>
634 <td><?php echo $c_achs[$a['key']][1]; ?></td>
635 <td><?php echo $a['value']; ?></td>
636 </tr>
637 <?php endforeach; ?>
638 </tbody>
639 </table>
640 <style type="text/css">
641 table.achievements,
642 #ac_toggle_hide {
643 display: none;
644 }
645 #ac_toggle_hide:checked + table.achievements {
646 display: table;
647 }
648 </style>
649 <script type="text/javascript">
650 document.getElementById("ac_label_hide").addEventListener("click", function(event){
651 event.preventDefault();
652 if (document.getElementById("ac_label_hide").innerHTML == "<?php echo str_replace('"', '\"', $toggle['show']); ?>") {
653 document.getElementById("ac_label_hide").innerHTML = "<?php echo str_replace('"', '\"', $toggle['hide']); ?>";
654 document.getElementById("ac_toggle_hide").checked = true;
655 } else {
656 document.getElementById("ac_label_hide").innerHTML = "<?php echo str_replace('"', '\"', $toggle['show']); ?>";
657 document.getElementById("ac_toggle_hide").checked = false;
658 }
659 });
660 </script>
661 <?php endif; ?>
662 <?php endif; ?>
663
664 <!-- DEATH LIST -->
665 <table class="deathlist">
666 <thead>
667 <tr class="yellow">
668 <th colspan="2"><?= t('char.death_list') ?></th>
669 </tr>
670 </thead>
671 <tbody>
672 <?php
673 $deaths = db()->fetchAll("
674 SELECT
675 `player_id`,
676 `time`,
677 `level`,
678 `killed_by`,
679 `is_player`,
680 `mostdamage_by`,
681 `mostdamage_is_player`,
682 `unjustified`,
683 `mostdamage_unjustified`
684 FROM `player_deaths`
685 WHERE `player_id` = ?
686 ORDER BY `time` DESC
687 LIMIT 10;
688 ", [$user_id]);
689
690 if ($deaths) {
691 foreach ($deaths as $d) {
692 $lasthit = ($d['is_player'])
693 ? "<a href='characterprofile.php?name=".$d['killed_by']."'>".$d['killed_by']."</a>"
694 : $d['killed_by'];
695
696 ?>
697 <tr>
698 <td><?php echo getClock($d['time'], true, true); ?></td>
699 <td>
700 <?php
701 echo t('char.killed_at', ['level' => $d['level'], 'killer' => $lasthit]);
702 if ($d['unjustified']) {
703 echo " <font color='red' style='font-style: italic;'>" . t('char.unjustified') . "</font>";
704 }
705 $mostdmg = ($d['mostdamage_by'] !== $d['killed_by']) ? true : false;
706 if ($mostdmg) {
707 $mostdmg = ($d['mostdamage_is_player'])
708 ? "<a href='characterprofile.php?name=".$d['mostdamage_by']."'>".$d['mostdamage_by']."</a>"
709 : $d['mostdamage_by'];
710
711 echo "<br>" . t('char.and_by', ['name' => $mostdmg]);
712
713 if ($d['mostdamage_unjustified']) {
714 echo " <font color='red' style='font-style: italic;'>" . t('char.unjustified') . "</font>";
715 }
716 } else {
717 echo " <b>" . t('char.soloed') . "</b>";
718 }
719 ?>
720 </td>
721 </tr>
722 <?php
723 }
724 } else {
725 ?>
726 <tr>
727 <td colspan="2"><?= t('char.never_died') ?></td>
728 </tr>
729 <?php
730 }
731 ?>
732 </tbody>
733 </table>
734
735 <!-- QUEST PROGRESSION -->
736 <?php
737 $totalquests = 0;
738 $completedquests = 0;
739 $firstrun = 1;
740
741 if ($config['EnableQuests'] == true) {
742 $sqlquests = db()->fetchAll("
743 SELECT `player_id`, `key`, `value`
744 FROM player_storage
745 WHERE `player_id` = ?
746 ", [$user_id]);
747 if (isset($config['quests']) && !empty($config['quests'])) {
748 foreach ($config['quests'] as $cquest) {
749 $totalquests = $totalquests + 1;
750 if ($sqlquests !== false) {
751 foreach ($sqlquests as $dbquest) {
752 if ($cquest[0] == $dbquest['key'] && $cquest[1] == $dbquest['value']) {
753 $completedquests = $completedquests + 1;
754 }
755 }
756 }
757 if ($cquest[3] == 1) {
758 if ($completedquests != 0) {
759 if ($firstrun == 1): ?>
760 <b> <?= t('char.quest_progression') ?> </b>
761 <table id="characterprofileQuest" class="table table-striped table-hover">
762 <thead>
763 <tr class="yellow">
764 <th><?= t('char.quest_label') ?></th>
765 <th><?= t('char.progression_label') ?></th>
766 </tr>
767 </thead>
768 <tbody>
769 <?php
770 $firstrun = 0;
771 endif;
772 $completed = $completedquests / $totalquests * 100;
773 ?>
774 <tr>
775 <td><?php echo $cquest[2]; ?></td>
776 <td id="progress">
777 <span id="percent"><?php echo round($completed); ?>%</span>
778 <div id="bar" style="width: '.$completed.'%"></div>
779 </td>
780 </tr>
781 <?php
782 }
783 $completedquests = 0;
784 $totalquests = 0;
785 }
786 }
787 }
788 }
789
790 if ($firstrun == 0): ?>
791 </tbody></table>
792 <?php endif; ?>
793 <!-- END QUEST PROGRESSION -->
794
795 <!-- CHARACTER LIST -->
796 <?php
797 // Load other visible characters
798 $otherChars = db()->fetchAll("
799 SELECT
800 `p`.`id`,
801 `p`.`name`,
802 `p`.`level`,
803 `p`.`vocation`,
804 `p`.`lastlogin`,
805 CASE WHEN `l`.`player_id` IS NULL THEN 0 else 1 END as `online`
806 FROM `players` as `o`
807 JOIN `players` as `p`
808 ON `o`.`account_id` = `p`.`account_id`
809 LEFT JOIN `znote_players` as `z`
810 ON `p`.`id` = `z`.`player_id`
811 LEFT JOIN `znote_players` as `z2`
812 ON `o`.`id` = `z2`.`player_id`
813 LEFT JOIN `players_online` as `l` ON `p`.`id` = `l`.`player_id`
814 WHERE `o`.`id` = ?
815 AND `p`.`id` != `o`.`id`
816 AND `z`.`hide_char` = 0
817 AND `z2`.`hide_char` = 0
818 ORDER BY `p`.`experience` DESC;
819 ", [$user_id]);
820
821 // Render table if there are any characters to show
822 if ($otherChars !== false) {
823 ?>
824 <li>
825 <b><?= t('char.other_chars') ?></b><br>
826 <table id="characterprofileTable" class="table table-striped table-hover">
827 <tr class="yellow">
828 <th><?= t('common.name_label') ?></th>
829 <th><?= t('common.level_label') ?></th>
830 <th><?= t('common.vocation_label') ?></th>
831 <th><?= t('char.last_login_label') ?></th>
832 <th><?= t('common.status_label') ?></th>
833 </tr>
834 <?php
835 // Add character rows
836 foreach ($otherChars as $char):
837 ?>
838 <tr>
839 <td><a href="characterprofile.php?name=<?php echo $char['name']; ?>"><?php echo $char['name']; ?></a></td>
840 <td><?php echo (int)$char['level']; ?></td>
841 <td><?php echo vocation_id_to_name($char['vocation']); ?></td>
842 <td><?php echo ($char['lastlogin'] != 0) ? getClock($char['lastlogin'], true, true) : t('char.never'); ?></td>
843 <td><?php echo ($char['online']) ? t('common.online') : t('common.offline'); ?></td>
844 </tr>
845 <?php
846 endforeach;
847 ?>
848 </table>
849 </li>
850 <?php
851 }
852 ?>
853 <!-- END CHARACTER LIST -->
854
855 <p class="address"><?= t('char.address') ?> <a href="<?php echo ($config['htwrite']) ? "//" . $_SERVER['HTTP_HOST']."/" . $profile_data['name'] : "//" . $_SERVER['HTTP_HOST'] . "/characterprofile.php?name=" . $profile_data['name']; ?>"><?php echo ($config['htwrite']) ? $_SERVER['HTTP_HOST']."/". $profile_data['name'] : $_SERVER['HTTP_HOST']."/characterprofile.php?name=". $profile_data['name']; ?></a></p>
856 <?php if (function_exists('tco_characterprofile_close')) { tco_characterprofile_close(); } ?>
857
858 <?php
859 } else {
860 echo t('char.does_not_exist', ['name' => htmlentities(strip_tags($name, ENT_QUOTES))]);
861 }
862} else {
863 view('character_search');
864}
865theme_close(); ?>
866
Top