Files
2026-08-02 17:55:21 +00:00

363 lines
21 KiB
Plaintext

{% extends '__layout__.html' %}
{% block title %}{{PlaceAssetObj.name}}{% endblock %}
{% block head %}
<link rel="stylesheet" href="/static/css/gameview.css">
<link rel="stylesheet" href="/static/css/icons.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">
<script src="/static/js/assetrate.js"></script>
<script src="/static/js/catalog.js?ver=4"></script>
<div id="X-CSRF-TOKEN" data-xcsrf="{{csrf_token()}}"></div>
<div id="csrf_token" data-csrf-token="{{ csrf_token() }}"></div>
{% endblock %}
{% block content %}
<div class="container" style="min-height:100vh;background-color:#1e1e1e;max-width:1100px;padding-top:24px;">
<div class="row ps-md-4 pe-md-4">
{% if UniverseObj.root_place_id != PlaceObj.placeid %}
<div class="alert alert-info">
<p class="m-0">This game is part of <a href="/games/{{UniverseObj.root_place_id}}/--" class="text-decoration-none">{{UniverseRootPlace.name}}</a>'s universe</p>
</div>
{% endif %}
<div class="col-md-8">
<img width="100%" class="rounded" style="aspect-ratio:16/9;" src="/Thumbs/Asset.ashx?assetId={{PlaceAssetObj.id}}&x=1280&y=720" alt="{{PlaceAssetObj.name}} Thumbnail">
</div>
<div class="col-md-4 position-relative" style="min-height:200px;">
<h1 class="m-0">{{PlaceAssetObj.name}}</h1>
{% if PlaceAssetObj.creator_type == 0 %}
<p class="m-0">By <a href="/users/{{PlaceAssetObj.creator_id}}/profile" class="text-decoration-none">{{CreatorObj.username}}</a></p>
{% else %}
<p class="m-0">By <a href="/groups/{{PlaceAssetObj.creator_id}}/--" class="text-decoration-none">{{CreatorObj.name}}</a></p>
{% endif %}
<div class="position-absolute w-100 p-2 border-top border-bottom mb-3" style="bottom:0;">
{% if UniverseObj.is_public: %}
{% if UserDoesNotHaveBC == False %}
{% if PlaceAssetObj.moderation_status != 2 or UniverseObj.moderation_status != 2 %}
{% if IsTooYoung: %}
<button class="btn btn-lg w-100 disabled" style="background-color:#555;color:#aaa;">PLAY <i class="bi bi-play-fill"></i></button>
<p class="m-0 w-100 text-center text-secondary" style="font-size:12px;">Your account must be at least {{MinAccountAge}} days old to play</p>
{% else %}
<button class="btn btn-lg w-100 gamelaunch-btn" data-placeid="{{PlaceAssetObj.id}}" style="background-color:#02b757;color:#fff;font-weight:700;">PLAY <i class="bi bi-play-fill"></i></button>
{% endif %}
{% else %}
<button class="btn btn-lg w-100 disabled" style="background-color:#555;color:#aaa;">PLAY <i class="bi bi-play-fill"></i></button>
<p class="m-0 w-100 text-center text-secondary" style="font-size:12px;">This place is currently under review</p>
{% endif %}
{% else %}
<button class="btn btn-lg w-100 disabled" style="background-color:#555;color:#aaa;">PLAY <i class="bi bi-play-fill"></i></button>
<p class="m-0 w-100 text-center text-secondary" style="font-size:12px;">A Builders Club membership is required</p>
{% endif %}
{%else%}
<button class="btn btn-lg w-100 disabled" style="background-color:#555;color:#aaa;">Play</button>
<p class="m-0 w-100 text-center text-secondary" style="font-size:12px;">This place has been set to private by its creator</p>
{%endif%}
<div class="d-flex align-items-center" style="height:36px;">
<div class="favorite-button-container" data-favorite-count="{{FavoriteCount}}" data-assetid="{{PlaceAssetObj.id}}" data-userfavorite-status="{{UserFavoriteStatus}}">
<div class="icon-favorite"></div>
<span title="{{FavoriteCount}}" class="text-favorite">{{FavoriteCount}}</span>
</div>
<div class="usersVote" data-assetid="{{PlaceAssetObj.id}}" data-likes="{{LikeCount}}" data-dislikes="{{DislikeCount}}" data-uservote-status="{{UserVoteStatus}}">
<div class="upvote">
<span class="icon-like cursor-pointer"></span>
</div>
<div class="votedetails">
<div class="votecontainer">
<div class="votebackground"></div>
<div class="votepercentage" style="width:50%;"></div>
<div>
<div class="segment" style="left:18%;"></div>
<div class="segment" style="left:38%;"></div>
<div class="segment" style="left:58%;"></div>
<div class="segment" style="left:78%;"></div>
</div>
</div>
<div class="votenumbers">
<div class="countleft"><span title="5" class="vote-up-text">5</span></div>
<div class="countright"><span title="5" class="vote-down-text">5</span></div>
</div>
</div>
<div class="downvote">
<span class="icon-dislike cursor-pointer"></span>
</div>
<p id="vote-feedback-text" style="display:none;font-size:12px;"></p>
</div>
</div>
</div>
</div>
</div>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<div>
{% for category, message in messages %}
{% if category == 'error': %}
<div class="alert alert-danger p-2 text-center">{{ message }}</div>
{% endif %}
{% if category == 'success': %}
<div class="alert alert-success p-2 text-center">{{ message }}</div>
{% endif %}
{% endfor %}
</div>
{% endif %}
{% endwith %}
<nav class="mt-4 m-2">
<div class="page-tabs" id="nav-tab" role="tablist">
<a href="#about" class="nav-link active" id="nav-about-tab" role="tab">About</a>
<a href="#store" class="nav-link" id="nav-store-tab" role="tab">Store</a>
<a href="#servers" class="nav-link" id="nav-servers-tab" role="tab">Servers</a>
</div>
</nav>
<div class="tab-content ps-2 pe-2 pt-1 pb-3" id="nav-tabContent">
<div class="tab-pane show active" id="nav-about" role="tabpanel">
<h2>Description</h2>
<div class="ms-2" style="min-height:200px;">
{% for line in SplittedDescription %}
<p class="text-secondary m-0" style="font-size:large;">{{line}}</p>
{% endfor %}
</div>
<div class="border-top border-bottom ms-2 me-2 pt-2 pb-2 row text-center details-container">
<div class="col"><p class="m-0">Active</p><h2>{{PlayerCount}}</h2></div>
<div class="col"><p class="m-0">Visits</p><h2>{{PlaceObj.visitcount}}</h2></div>
<div class="col"><p class="m-0">Created</p><h2>{{ConvertDatetimeToDayMonthYear(PlaceAssetObj.created_at)}}</h2></div>
<div class="col"><p class="m-0">Updated</p><h2>{{ConvertDatetimeToDayMonthYear(PlaceAssetObj.updated_at)}}</h2></div>
<div class="col"><p class="m-0">Server Size</p><h2>{{PlaceObj.maxplayers}}</h2></div>
</div>
{% if len(GameBadges) != 0: %}
<div class="mt-4">
<h3 class="m-0">Badges</h3>
<div class="p-1" style="max-height:400px;overflow-y:auto;">
{% for badgeObj in GameBadges: %}
<a href="/badges/{{badgeObj.id}}/--" class="text-decoration-none">
<div class="game-badge-row">
<img style="background-color:#1e1e1e;border-radius:100%;flex-shrink:0;" src="/Thumbs/Asset.ashx?assetId={{badgeObj.icon_image_id}}&x=100&y=100" width="100" height="100" alt="{{badgeObj.name}}">
<div>
<h4 class="m-0">{{badgeObj.name}}</h4>
<p class="m-0 text-secondary" style="font-size:14px;height:46px;">{{badgeObj.description}}</p>
{% if DoesUserOwnBadge(badgeObj.id, currentuser.id) %}
<p class="m-0 text-white" style="font-size:13px;">You own this badge</p>
{% endif %}
</div>
<div class="ms-auto me-2 mt-auto mb-auto">
<ul style="display:grid;min-width:200px;grid:repeat(3,33.333%) / max-content auto;font-size:15px;">
<li style="display:contents;">
<p class="m-0 text-secondary text-end">Rarity</p>
<p class="m-0 text-white fw-bold text-center">{{ round(CalculateBadgeRarity(badgeObj.id) * 100, 1) }}%</p>
</li>
<li style="display:contents;">
<p class="m-0 text-secondary text-end">Won Yesterday</p>
<p class="m-0 text-white fw-bold text-center">{{ GetBadgeAwardedPastDay(badgeObj.id) }}</p>
</li>
<li style="display:contents;">
<p class="m-0 text-secondary text-end">Won Ever</p>
<p class="m-0 text-white fw-bold text-center">{{ GetTotalBadgeAwardedCount(badgeObj.id) }}</p>
</li>
</ul>
</div>
</div>
</a>
{% endfor %}
</div>
</div>
{% endif %}
</div>
<div class="tab-pane" id="nav-store" role="tabpanel">
<h4 class="m-0 mb-2">Gamepasses</h4>
<div class="d-flex" style="flex-wrap:wrap;justify-content:flex-start;gap:10px;">
{% for gamepass in Gamepasses: %}
{% if gamepass.gamepass.is_for_sale: %}
<div class="gamepass-card">
<a href="/library/{{gamepass.gamepass_id}}/" class="text-decoration-none">
<img style="aspect-ratio:1/1;width:100%;background-color:#161616;border-radius:100%;" src="/Thumbs/Asset.ashx?assetId={{gamepass.gamepass_id}}&x=180&y=180" alt="{{gamepass.gamepass.name}}">
<h5 class="m-0 mt-1 w-100 text-truncate">{{gamepass.gamepass.name}}</h5>
</a>
<p class="text-robux m-0">R$ {{gamepass.gamepass.price_robux}}</p>
{% if DoesUserownGamepass(gamepass.gamepass_id, currentuser.id) %}
<button class="btn btn-sm w-100 mt-2 disabled" style="background-color:#333;color:#aaa;">Owned</button>
{% else %}
<button class="btn btn-sm w-100 mt-2 purchase-button" style="background-color:#e3e3e3;color:#111;font-weight:600;" data-asset-id="{{gamepass.gamepass_id}}" data-asset-name="{{gamepass.gamepass.name}}" data-expected-price="{{gamepass.gamepass.price_robux}}" data-currency-type="0">Purchase</button>
{% endif %}
</div>
{% endif %}
{%endfor%}
</div>
{% if len(Gamepasses) == 0: %}
<p class="w-100 text-center mt-5 mb-5">No Gamepasses Found</p>
{% endif %}
</div>
<div class="tab-pane" id="nav-servers" role="tabpanel">
<h2>Servers</h2>
<div class="w-100">
{% for PlaceServer in ActiveServers: %}
<div class="server-row">
<div class="d-flex align-items-center w-100">
<div>
<h5 class="m-0">{{PlaceServer.playercount}} of {{PlaceServer.maxplayercount}} players</h5>
<p class="text-secondary m-0" style="font-size:10px;">{{PlaceServer.id}} - {{PlaceServer.host}}{% if PlaceServer.is_reserved_server %} - Reserved Server{%endif%}</p>
</div>
<div class="ms-auto">
{% if PlaceServer.is_reserved_server == False %}
<button class="btn btn-sm btn-secondary gamelaunch-btn" data-placeid="{{PlaceAssetObj.id}}" data-jobid="{{PlaceServer.id}}">Join Server</button>
{% else %}
<button class="btn btn-sm btn-secondary disabled">Join Server</button>
{% endif %}
</div>
{% if CanShutdownServer %}
<form action="/games/{{PlaceAssetObj.id}}/shutdown-server/{{PlaceServer.id}}" method="post" class="ms-2">
<input type="hidden" name="csrf_token" value="{{csrf_token()}}">
<button class="btn btn-sm btn-danger" type="submit">Shutdown</button>
</form>
{%endif%}
</div>
<div class="server-players-row">
{% for Player in PlaceServer.players: %}
<a href="/users/{{Player.userid}}/profile" style="border-radius:3px;background:#1a1a1a;padding:4px;">
<img width="60" height="60" src="/Thumbs/Head.ashx?x=60&y=60&userId={{Player.userid}}" style="border-radius:3px;" alt="{{Player.user.username}}">
</a>
{% endfor%}
{% if PlaceServer.playercount == 0 :%}
<p class="m-0 text-center w-100 mb-auto mt-auto" style="font-size:12px;">Empty Server</p>
{% endif %}
</div>
<div class="server-fill-bar">
{% if PlaceServer.playercount != 0 %}
<div class="server-fill-inner" style="width:{{PlaceServer.playercount / PlaceServer.maxplayercount * 100}}%;"></div>
{% endif %}
</div>
</div>
{%endfor%}
{% if ActiveServerCount == 0: %}
<p class="w-100 text-center">No Active Servers Found</p>
{%endif%}
</div>
</div>
</div>
</div>
<script>
(function(){
async function GenerateAuthTicket(){
var r=await fetch("/Login/NewAuthTicket",{method:"POST"});
return await r.text();
}
function initTabs(){
var tabs=document.querySelectorAll(".page-tabs .nav-link");
var panes=document.querySelectorAll(".tab-content .tab-pane");
function activate(tab){
tabs.forEach(function(t){t.classList.remove("active");});
panes.forEach(function(p){p.classList.remove("show","active");});
tab.classList.add("active");
var target=document.querySelector(tab.getAttribute("href"));
if(target){target.classList.add("show","active");}
}
tabs.forEach(function(tab){
tab.addEventListener("click",function(e){
e.preventDefault();
activate(tab);
window.location.hash=tab.getAttribute("href");
});
});
var hash=window.location.hash;
if(hash){
var matchTab=document.querySelector('.page-tabs .nav-link[href="'+hash+'"]');
if(matchTab){activate(matchTab);}
}
}
function initLaunch(){
var overlay=document.getElementById("transparent-background-top");
var closeBtn=document.getElementById("close-launcher-btn");
var loadingBar=document.getElementById("launch-loading-bar");
var downloadBtn=document.getElementById("launch-download-btn");
if(!overlay)return;
closeBtn.addEventListener("click",function(){overlay.style.display="none";});
closeBtn.style.display="none";
document.querySelectorAll(".gamelaunch-btn").forEach(function(el){
el.addEventListener("click",async function(){
overlay.style.display="flex";
loadingBar.style.display="block";
downloadBtn.style.display="none";
closeBtn.style.display="none";
var placeid=el.getAttribute("data-placeid");
var authTicket=await GenerateAuthTicket();
var authticket2=await GenerateAuthTicket();
var clientyear={{UniverseObj.place_year.value}};
var joinScriptUrl;
if(clientyear==2014){
joinScriptUrl="http://www.syntax.eco/game/2014Join.lua?placeId="+placeid;
}else{
joinScriptUrl="https://www.syntax.eco/Game/placelauncher.ashx?placeId="+placeid+"&t="+authticket2;
}
var jobid=el.getAttribute("data-jobid");
if(jobid)joinScriptUrl+="&jobId="+jobid;
var needsSlash=false;
var isFirefox=navigator.userAgent.toLowerCase().indexOf("firefox")>-1;
if(isFirefox){
var ffver=parseInt(navigator.userAgent.toLowerCase().split("firefox/")[1]);
if(ffver>121)needsSlash=true;
}
window.location.href="syntax-player://"+(needsSlash?"/":"")+"1+launchmode:play+gameinfo:"+authTicket+"+placelauncherurl:"+joinScriptUrl+"+k:l+clientyear:"+clientyear;
setTimeout(function(){
loadingBar.style.display="none";
downloadBtn.style.display="block";
closeBtn.style.display="block";
setTimeout(function(){overlay.style.display="none";},6000);
},4000);
});
});
}
function initPurchaseModal(){
var modal=document.getElementById("purchaseModal");
if(!modal)return;
document.querySelectorAll(".purchase-button").forEach(function(btn){
btn.addEventListener("click",function(){
document.getElementById("purchase-modal-title").textContent="Confirm Purchase";
document.getElementById("purchase-modal-content").textContent="Purchase "+btn.getAttribute("data-asset-name")+" for R$ "+btn.getAttribute("data-expected-price")+"?";
modal.classList.add("open");
});
});
document.getElementById("purchase-modal-close").addEventListener("click",function(){modal.classList.remove("open");});
modal.addEventListener("click",function(e){if(e.target===modal)modal.classList.remove("open");});
}
document.addEventListener("DOMContentLoaded",function(){
initTabs();
initLaunch();
initPurchaseModal();
});
})();
</script>
<div class="launch-modal-overlay" id="transparent-background-top" style="display:none;">
<div class="launch-modal-box">
<img src="/static/img/SyntaxLogo.png" width="90" height="90" alt="Nexium">
<div id="launch-loading-bar" style="width:100%;">
<p class="m-0 text-center">Nexium is now loading. Get ready to play!</p>
<div class="ps-2 pe-2 w-100">
<div class="progress mt-2">
<div class="progress-bar" style="width:100%;"></div>
</div>
</div>
</div>
<div id="launch-download-btn" style="display:none;width:100%;">
<p class="m-0 text-center">You're moments away from getting into the game!</p>
<div class="ps-3 pe-3 mt-2">
<a class="btn w-100 text-decoration-none" href="/download" style="background-color:#02b757;color:#fff;font-weight:700;">Download and Install Nexium</a>
</div>
</div>
<button class="btn" id="close-launcher-btn" style="position:absolute;top:10px;right:10px;background:none;border:none;color:#aaa;font-size:1.2rem;">✕</button>
</div>
</div>
<div class="purchase-modal" id="purchaseModal">
<div class="purchase-modal-box">
<div class="purchase-modal-header">
<h5 class="m-0" id="purchase-modal-title">Confirm Purchase</h5>
<button class="btn" id="purchase-modal-close" style="background:none;border:none;color:#aaa;font-size:1.1rem;">✕</button>
</div>
<div class="purchase-modal-body" id="purchase-modal-content"></div>
<div class="purchase-modal-footer">
<button class="btn btn-secondary" id="purchase-modal-close-2">Close</button>
<button class="btn btn-primary" id="purchase-modal-btn">Purchase</button>
</div>
</div>
</div>
{% endblock %}