merge master
This commit is contained in:
@@ -1,14 +1,14 @@
|
||||
header.svelte-7t4byu.svelte-7t4byu{overflow-y:hidden}.grid-container.svelte-7t4byu.svelte-7t4byu{position:absolute;left:0;top:0;box-sizing:border-box;width:100%;height:100%;white-space:nowrap;margin-bottom:0px;overflow:hidden;padding:20px 40px;margin:0px;display:grid;grid-template-columns:repeat(12, 1fr);grid-template-rows:1fr 1fr 1fr 1fr 1fr;align-items:center}header.svelte-7t4byu h1.svelte-7t4byu{grid-column:1 / 7;grid-row:1;padding:20px;border:1px solid bisque}header.svelte-7t4byu nav.svelte-7t4byu{grid-column:7 / 13;grid-row:1;justify-self:end;padding:20px;border:1px solid bisque}header.svelte-7t4byu h2.svelte-7t4byu{grid-row:3;grid-column:5 / span 4;justify-self:center;border:1px solid black;z-index:3}header.svelte-7t4byu h2 div.svelte-7t4byu{font-size:2em}nav.svelte-7t4byu div.svelte-7t4byu{display:inline;color:bisque;font-weight:bold}nav.svelte-7t4byu>div.svelte-7t4byu{padding-left:1em}nav.svelte-7t4byu div.svelte-7t4byu:hover{text-decoration:underline;cursor:pointer}main.svelte-1cznfcz.svelte-1cznfcz{text-align:center;padding-top:40px;padding-bottom:40px}form.svelte-1cznfcz.svelte-1cznfcz{padding-top:15px}form.svelte-1cznfcz input.svelte-1cznfcz{max-width:330px}.error.svelte-1cznfcz.svelte-1cznfcz{font-weight:bold;font-size:0.8em;color:red}div.wrapper.svelte-1q8uute{display:flexbox;align-items:center}div.wrapper.svelte-1q8uute{display:flexbox;align-items:center}canvas.svelte-1bstsd0{width:100%;height:100%;background-color:#666}@font-face{font-family:'Bondi';src:url('/fonts/Bondi.ttf.woff') format('woff'),
|
||||
url('/fonts/Bondi.ttf.svg#Bondi') format('svg'),
|
||||
url('/fonts/Bondi.ttf.eot'),
|
||||
url('/fonts/Bondi.ttf.eot?#iefix') format('embedded-opentype');font-weight:normal;font-style:normal}header.svelte-1gjmpio.svelte-1gjmpio{background:#618174;margin:0}header.svelte-1gjmpio.svelte-1gjmpio{position:sticky;display:grid;grid-template-columns:1fr 1fr 1fr}h1.svelte-1gjmpio.svelte-1gjmpio{font-family:'Bondi'}h1.svelte-1gjmpio.svelte-1gjmpio{margin:0;text-align:left;display:flex;justify-self:center;align-self:center}img.svelte-1gjmpio.svelte-1gjmpio{cursor:pointer;max-width:40px;padding:7px 20px;justify-self:left}nav.svelte-1gjmpio.svelte-1gjmpio{display:flex;justify-content:right}nav.svelte-1gjmpio button.svelte-1gjmpio{margin:7px 20px;border-radius:4px}div.outer.svelte-16aefqu{max-width:960px;margin:40px auto}:root{--purple:rgb(123, 31, 162);--violet:rgb(103, 58, 183);--pink:rgb(244, 143, 177)}@keyframes svelte-16aefqu-background-pan{from{background-position:0% center}to{background-position:-200% center}}@keyframes svelte-16aefqu-scale{from,to{transform:scale(0)}50%{transform:scale(1)}}@keyframes svelte-16aefqu-rotate{from{transform:rotate(0deg)}to{transform:rotate(180deg)}}main.svelte-qtbld7{text-align:center}div.cards.svelte-qtbld7{display:grid;grid-template-columns:1fr 1fr;grid-gap:20px}img.svelte-qtbld7{width:60px}form.svelte-qtbld7{text-align:center}.form-field.svelte-qtbld7{padding:10px}.label.svelte-qtbld7{font-weight:bold}.inline-check.svelte-qtbld7{display:inline}.error.svelte-qtbld7{font-size:0.8em;font-weight:bold;color:red}.success.svelte-qtbld7{font-size:0.8em;font-weight:bold;color:green}div.top-grid.svelte-55f7si{display:grid;grid-template-columns:repeat(12, 1fr);height:85vh}div.all-users-sidebar.svelte-55f7si{grid-column:1 / span 2;background:white}div.a-user.svelte-55f7si{display:inline-block}div.status.svelte-55f7si{font-size:0.6em;font-weight:bold}div[class^="a-user"].svelte-55f7si:hover{text-decoration:underline;font-weight:bold;cursor:pointer}div.main-display.svelte-55f7si{grid-column:3 / span 10}.error.svelte-55f7si{font-size:0.8em;font-weight:bold;color:red}div.outer.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu{max-width:960px;margin:40px auto}main.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu{max-width:960px;margin:40px auto;text-align:center}.avatar.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu{max-width:150px}section.main-stats.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu{max-width:600px;margin:40px auto;text-align:center;display:grid;grid-template-columns:repeat(3, 1fr);grid-template-rows:repeat(3, 1fr)}section.main-stats.svelte-16aefqu h4.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu{grid-column:1 / span 3}div.username.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu{font-size:1.5em;font-weight:bold;padding-bottom:5px}div.rank.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu{font-size:1.2em;font-weight:bold}:root{--purple:rgb(123, 31, 162);--violet:rgb(103, 58, 183);--pink:rgb(244, 143, 177)}@keyframes svelte-16aefqu-background-pan{from{background-position:0% center}to{background-position:-200% center}}@keyframes svelte-16aefqu-scale{from,to{transform:scale(0)}50%{transform:scale(1)}}@keyframes svelte-16aefqu-rotate{from{transform:rotate(0deg)}to{transform:rotate(180deg)}}div.svelte-16aefqu>.glitter.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu{display:inline-block;position:relative}div.svelte-16aefqu>.glitter.svelte-16aefqu>.glitter-star.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu{--size:clamp(20px, 1.5vw, 30px);animation:svelte-16aefqu-scale 700ms ease forwards;display:block;height:var(--size);left:var(--star-left);position:absolute;top:var(--star-top);width:var(--size)}div.svelte-16aefqu>.glitter.svelte-16aefqu>.glitter-star.svelte-16aefqu>svg.svelte-16aefqu.svelte-16aefqu{animation:svelte-16aefqu-rotate 1000ms linear infinite;display:block;opacity:0.7}div.svelte-16aefqu>.glitter.svelte-16aefqu>.glitter-star.svelte-16aefqu>svg.svelte-16aefqu>path.svelte-16aefqu{fill:var(--violet)}div.svelte-16aefqu>.glitter.svelte-16aefqu>.glitter-text.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu{animation:svelte-16aefqu-background-pan 3s linear infinite;background:linear-gradient(
|
||||
url('/fonts/Bondi.ttf.eot?#iefix') format('embedded-opentype');font-weight:normal;font-style:normal}header.svelte-1aisfio.svelte-1aisfio{background:#618174;margin:0}header.svelte-1aisfio.svelte-1aisfio{position:sticky;display:grid;grid-template-columns:1fr 1fr 1fr}h1.svelte-1aisfio.svelte-1aisfio{font-family:'Bondi'}h1.svelte-1aisfio.svelte-1aisfio{margin:0;text-align:left;display:flex;justify-self:center;align-self:center}img.svelte-1aisfio.svelte-1aisfio{cursor:pointer;max-width:40px;padding:7px 20px;justify-self:left}nav.svelte-1aisfio.svelte-1aisfio{display:flex;justify-content:right}nav.svelte-1aisfio button.svelte-1aisfio{margin:7px 20px;border-radius:4px}div.outer.svelte-1tyjf3q{max-width:960px;margin:40px auto}:root{--purple:rgb(123, 31, 162);--violet:rgb(103, 58, 183);--pink:rgb(244, 143, 177)}@keyframes svelte-1tyjf3q-background-pan{from{background-position:0% center}to{background-position:-200% center}}@keyframes svelte-1tyjf3q-scale{from,to{transform:scale(0)}50%{transform:scale(1)}}@keyframes svelte-1tyjf3q-rotate{from{transform:rotate(0deg)}to{transform:rotate(180deg)}}main.svelte-qtbld7{text-align:center}div.cards.svelte-qtbld7{display:grid;grid-template-columns:1fr 1fr;grid-gap:20px}img.svelte-qtbld7{width:60px}form.svelte-qtbld7{text-align:center}.form-field.svelte-qtbld7{padding:10px}.label.svelte-qtbld7{font-weight:bold}.inline-check.svelte-qtbld7{display:inline}.error.svelte-qtbld7{font-size:0.8em;font-weight:bold;color:red}.success.svelte-qtbld7{font-size:0.8em;font-weight:bold;color:green}div.top-grid.svelte-55f7si{display:grid;grid-template-columns:repeat(12, 1fr);height:85vh}div.all-users-sidebar.svelte-55f7si{grid-column:1 / span 2;background:white}div.a-user.svelte-55f7si{display:inline-block}div.status.svelte-55f7si{font-size:0.6em;font-weight:bold}div[class^="a-user"].svelte-55f7si:hover{text-decoration:underline;font-weight:bold;cursor:pointer}div.main-display.svelte-55f7si{grid-column:3 / span 10}.error.svelte-55f7si{font-size:0.8em;font-weight:bold;color:red}div.outer.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu{max-width:960px;margin:40px auto}main.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu{max-width:960px;margin:40px auto;text-align:center}.avatar.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu{max-width:150px}section.main-stats.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu{max-width:600px;margin:40px auto;text-align:center;display:grid;grid-template-columns:repeat(3, 1fr);grid-template-rows:repeat(3, 1fr)}section.main-stats.svelte-16aefqu h4.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu{grid-column:1 / span 3}div.username.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu{font-size:1.5em;font-weight:bold;padding-bottom:5px}div.rank.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu{font-size:1.2em;font-weight:bold}:root{--purple:rgb(123, 31, 162);--violet:rgb(103, 58, 183);--pink:rgb(244, 143, 177)}@keyframes svelte-16aefqu-background-pan{from{background-position:0% center}to{background-position:-200% center}}@keyframes svelte-16aefqu-scale{from,to{transform:scale(0)}50%{transform:scale(1)}}@keyframes svelte-16aefqu-rotate{from{transform:rotate(0deg)}to{transform:rotate(180deg)}}div.svelte-16aefqu>.glitter.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu{display:inline-block;position:relative}div.svelte-16aefqu>.glitter.svelte-16aefqu>.glitter-star.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu{--size:clamp(20px, 1.5vw, 30px);animation:svelte-16aefqu-scale 700ms ease forwards;display:block;height:var(--size);left:var(--star-left);position:absolute;top:var(--star-top);width:var(--size)}div.svelte-16aefqu>.glitter.svelte-16aefqu>.glitter-star.svelte-16aefqu>svg.svelte-16aefqu.svelte-16aefqu{animation:svelte-16aefqu-rotate 1000ms linear infinite;display:block;opacity:0.7}div.svelte-16aefqu>.glitter.svelte-16aefqu>.glitter-star.svelte-16aefqu>svg.svelte-16aefqu>path.svelte-16aefqu{fill:var(--violet)}div.svelte-16aefqu>.glitter.svelte-16aefqu>.glitter-text.svelte-16aefqu.svelte-16aefqu.svelte-16aefqu{animation:svelte-16aefqu-background-pan 3s linear infinite;background:linear-gradient(
|
||||
to right,
|
||||
var(--purple),
|
||||
var(--violet),
|
||||
var(--pink),
|
||||
var(--purple)
|
||||
);background-size:200%;-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;color:transparent;white-space:nowrap}.card.svelte-8smyff{background:white;padding:20px;border-radius:6px;box-shadow:0px 2px 4px rgba(0,0,0,0.1)}button.svelte-1u0z9cq{border:0;cursor:pointer;border-radius:6px;padding:8px 12px;font-weight:bold;box-shadow:1px 2px 3px rgba(0,0,0,0.2)}.primary.svelte-1u0z9cq{background:#d91b42;color:white}.secondary.svelte-1u0z9cq{background:#45c496;color:white}.flat.svelte-1u0z9cq{box-shadow:none}.primary.inverse.svelte-1u0z9cq{color:#d91b42;background:white;border:2px solid #d91b42}.secondary.inverse.svelte-1u0z9cq{color:#45c496;background:white;border:2px solid #45c496}.chat_box.svelte-zxenra.svelte-zxenra{display:flex;position:fixed;bottom:20px;right:20px;padding:5px;width:300px;height:400px;border:1px solid black}.chat.svelte-zxenra.svelte-zxenra{grid-area:chat}.chat_box.close.svelte-zxenra .grid_box.svelte-zxenra{gap:0px;grid:' chat ' auto
|
||||
/ auto }.chat_box.close.svelte-zxenra.svelte-zxenra{padding:0px;width:auto;height:auto}.chat_box.svelte-zxenra *{-ms-overflow-style:none;scrollbar-width:none}.chat_box.svelte-zxenra *::-webkit-scrollbar{display:none}.chat_box.svelte-zxenra .grid_box{display:grid;margin:0px;gap:5px;width:100%;height:100%}.chat_box.svelte-zxenra .grid_box *{display:flex;flex-direction:column;position:relative;box-sizing:border-box}.chat_box.svelte-zxenra .grid_box p{padding:10px;font-size:15px}.chat_box.svelte-zxenra .panel{overflow-y:scroll}.chat_box.svelte-zxenra .panel > *{margin-top:10px;margin-bottom:10px}.chat_box.svelte-zxenra .__show_if_only_child{display:none}.chat_box.svelte-zxenra .__show_if_only_child:only-child{display:flex;color:rgb(100, 100, 100)}.chat_box.svelte-zxenra .__center{margin-left:auto;margin-right:auto}.chat_box.svelte-zxenra .__border_top{border-top:1px solid black}.chat_box.svelte-zxenra .__check_change_next:checked ~ .__to_show{display:flex}.chat_box.svelte-zxenra .__check_change_next:checked ~ .__to_block,.chat_box.svelte-zxenra .__check_change_next:checked ~ .__to_block *{pointer-events:none;color:rgb(100, 100, 100)}.chat_box.svelte-zxenra .__to_show{display:none}button.svelte-j5z24r.svelte-j5z24r{display:flex;padding:0px;margin:auto;width:100%;cursor:pointer;outline:none;border:none;background-color:rgb(220, 220, 220)}button.svelte-j5z24r p.svelte-j5z24r{width:100%;margin:auto;text-align:center}button.svelte-j5z24r.svelte-j5z24r:hover{background-color:rgb(200, 200, 200)}button.svelte-j5z24r.svelte-j5z24r:active{background-color:rgb(190, 190, 190)}.list.svelte-j5z24r.svelte-j5z24r:not(:hover){background-color:rgb(240, 240, 240)}.list.svelte-j5z24r p.svelte-j5z24r{text-align:left}.transparent.svelte-j5z24r.svelte-j5z24r:not(:hover){background-color:transparent}.deactivate.svelte-j5z24r.svelte-j5z24r{background-color:transparent;pointer-events:none}.icon.svelte-j5z24r p.svelte-j5z24r{display:none}.icon.svelte-j5z24r.svelte-j5z24r:not(:hover){background-color:transparent}.icon.svelte-j5z24r.svelte-j5z24r{width:30px;height:100%;padding:0px}.dots.svelte-j5z24r.svelte-j5z24r::after{content:'\2807';font-size:20px;position:absolute;top:50%;left:0px;width:100%;height:auto;text-align:center;transform:translateY(-50%);cursor:pointer}.close.svelte-j5z24r.svelte-j5z24r::before{content:"";position:absolute;top:calc(50% - 1px);left:5px;width:20px;height:2px;background-color:black}.back.svelte-j5z24r.svelte-j5z24r::before{content:"";position:absolute;top:calc(50% - 6px - 1px);left:6px;width:14px;height:14px;border-left:1px solid black;border-bottom:1px solid black;transform:rotate(45deg)}.blocked.svelte-j5z24r.svelte-j5z24r{padding-left:30px}.blocked.svelte-j5z24r.svelte-j5z24r::before{content:"";position:absolute;top:calc(50% - 2px);left:10px;cursor:pointer;width:13px;height:10px;border-radius:2px;background-color:rgb(110, 110, 110)}.blocked.svelte-j5z24r.svelte-j5z24r::after{content:"";position:absolute;top:calc(50% - 9px);left:12px;cursor:pointer;width:9px;height:13px;border-radius:5px;box-sizing:border-box;border:3px solid rgb(110, 110, 110)}.grid_box.svelte-1jygwt2 .settings {grid-area:settings}.grid_box.svelte-1jygwt2 .close {grid-area:close}.grid_box.svelte-1jygwt2 .new {grid-area:new}.grid_box.svelte-1jygwt2 .panel_home{grid-area:panel_home}.grid_box.svelte-1jygwt2.svelte-1jygwt2{grid:' settings new close ' auto
|
||||
);background-size:200%;-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;color:transparent;white-space:nowrap}.card.svelte-8smyff{background:white;padding:20px;border-radius:6px;box-shadow:0px 2px 4px rgba(0,0,0,0.1)}button.svelte-1u0z9cq{border:0;cursor:pointer;border-radius:6px;padding:8px 12px;font-weight:bold;box-shadow:1px 2px 3px rgba(0,0,0,0.2)}.primary.svelte-1u0z9cq{background:#d91b42;color:white}.secondary.svelte-1u0z9cq{background:#45c496;color:white}.flat.svelte-1u0z9cq{box-shadow:none}.primary.inverse.svelte-1u0z9cq{color:#d91b42;background:white;border:2px solid #d91b42}.secondary.inverse.svelte-1u0z9cq{color:#45c496;background:white;border:2px solid #45c496}.chat_box.svelte-auy6qm{display:flex;position:fixed;bottom:20px;right:20px;padding:0px;width:auto;height:auto;border:1px solid black}.chat_box.svelte-auy6qm *{-ms-overflow-style:none;scrollbar-width:none}.chat_box.svelte-auy6qm *::-webkit-scrollbar{display:none}.chat_box.svelte-auy6qm .grid_box{display:grid;margin:5px;gap:5px;width:300px;height:400px}.chat_box.svelte-auy6qm .grid_box *{display:flex;flex-direction:column;position:relative;box-sizing:border-box}.chat_box.svelte-auy6qm .grid_box p{padding:10px;font-size:15px}.chat_box.svelte-auy6qm .panel{overflow-y:scroll}.chat_box.svelte-auy6qm .panel > *{margin-top:10px;margin-bottom:10px}.chat_box.svelte-auy6qm .__show_if_only_child{display:none}.chat_box.svelte-auy6qm .__show_if_only_child:only-child{display:flex;color:rgb(100, 100, 100)}.chat_box.svelte-auy6qm .__center{margin-left:auto;margin-right:auto}.chat_box.svelte-auy6qm .__border_top{border-top:1px solid black}.chat_box.svelte-auy6qm .__check_change_next:checked ~ .__to_show{display:flex}.chat_box.svelte-auy6qm .__check_change_next:checked ~ .__to_block,.chat_box.svelte-auy6qm .__check_change_next:checked ~ .__to_block *{pointer-events:none;color:rgb(100, 100, 100)}.chat_box.svelte-auy6qm .__to_show{display:none}.grid_box.svelte-fc4a40 .chat{grid-area:chat}.grid_box.svelte-fc4a40{gap:0px;grid:' chat ' auto
|
||||
/ auto }.chat_box.close .grid_box.svelte-fc4a40{margin:0px;width:auto;height:auto}.grid_box.svelte-1jygwt2 .settings {grid-area:settings}.grid_box.svelte-1jygwt2 .close {grid-area:close}.grid_box.svelte-1jygwt2 .new {grid-area:new}.grid_box.svelte-1jygwt2 .panel_home{grid-area:panel_home}.grid_box.svelte-1jygwt2.svelte-1jygwt2{grid:' settings new close ' auto
|
||||
' panel_home panel_home panel_home ' 1fr
|
||||
/ auto 1fr auto }.panel_home.svelte-1jygwt2 p.title.svelte-1jygwt2{margin:10px auto 0px auto}.grid_box.svelte-rmdfjs .back {grid-area:back}.grid_box.svelte-rmdfjs .room_name {grid-area:room_name}.grid_box.svelte-rmdfjs .close {grid-area:close}.grid_box.svelte-rmdfjs .panel_msg {grid-area:panel_msg}.grid_box.svelte-rmdfjs .send {grid-area:send}.grid_box.svelte-rmdfjs .panel_write{grid-area:panel_write}.grid_box.svelte-rmdfjs.svelte-rmdfjs{grid:' back room_name room_name close ' auto
|
||||
' panel_msg panel_msg panel_msg panel_msg ' 1fr
|
||||
@@ -28,5 +28,6 @@ header.svelte-7t4byu.svelte-7t4byu{overflow-y:hidden}.grid-container.svelte-7t4b
|
||||
/ auto 1fr auto }form.svelte-yo0any input[type=checkbox].svelte-yo0any.svelte-yo0any{display:none}form.svelte-yo0any label._checkbox.svelte-yo0any.svelte-yo0any{margin:0px auto 0px 10px;padding-left:10px;cursor:pointer}form.svelte-yo0any label._checkbox.svelte-yo0any.svelte-yo0any::after{content:"";position:absolute;top:calc(50% - 6px);left:0px;width:12px;height:12px;border:2px solid rgb(150, 150, 150);box-sizing:border-box;cursor:pointer}form.svelte-yo0any input[type=checkbox].svelte-yo0any:checked+label._checkbox.svelte-yo0any::after{background-color:rgb(200, 200, 200)}form.svelte-yo0any label._select.svelte-yo0any.svelte-yo0any{flex-direction:row}form.svelte-yo0any label._select p.svelte-yo0any.svelte-yo0any{margin:0px}form.svelte-yo0any select.svelte-yo0any.svelte-yo0any{margin:auto auto auto 10px;background-color:rgb(220, 220, 220);border:none;padding:5px;cursor:pointer}form.svelte-yo0any select.svelte-yo0any.svelte-yo0any:hover{background-color:rgb(200, 200, 200)}form.svelte-yo0any input[type=submit].svelte-yo0any.svelte-yo0any{margin-top:20px}.grid_box.svelte-1fj8iha .back {grid-area:back}.grid_box.svelte-1fj8iha .user {grid-area:user}.grid_box.svelte-1fj8iha .close {grid-area:close}.grid_box.svelte-1fj8iha .room_name {grid-area:room_name}.grid_box.svelte-1fj8iha .panel_user{grid-area:panel_user}.grid_box.svelte-1fj8iha{grid:' back user close ' auto
|
||||
' room_name room_name room_name ' auto
|
||||
' panel_user panel_user panel_user ' 1fr
|
||||
/ auto 1fr auto }.panel_user.svelte-1fj8iha{margin-top:-5px}.chat_msg.svelte-14xxpbz.svelte-14xxpbz{margin:5px auto;padding:5px;border-radius:5px}.chat_msg.svelte-14xxpbz.svelte-14xxpbz{margin-left:0px;background-color:rgb(210, 210, 210);max-width:80%}.chat_msg.svelte-14xxpbz p.svelte-14xxpbz{padding:0px}.chat_msg.svelte-14xxpbz p.name.svelte-14xxpbz{margin:0px;font-size:12px;color:rgb(100, 100, 100)}.chat_msg.svelte-14xxpbz p.msg.svelte-14xxpbz{margin:5px 0px}.chat_msg.svelte-14xxpbz p.msg.svelte-14xxpbz *{display:inline}.chat_msg.me.svelte-14xxpbz.svelte-14xxpbz{margin-right:0px;margin-left:auto;background-color:rgb(210, 110, 10)}.chat_msg.me.svelte-14xxpbz p.name.svelte-14xxpbz{display:none}.chat_msg.SERVER.svelte-14xxpbz.svelte-14xxpbz{margin-left:auto;background-color:transparent}.chat_msg.SERVER.svelte-14xxpbz p.name.svelte-14xxpbz{display:none}.chat_msg.SERVER.svelte-14xxpbz p.msg.svelte-14xxpbz{margin:0px auto;font-size:12px;color:rgb(100, 100, 100)}.chat_box.svelte-auy6qm{display:flex;position:fixed;bottom:20px;right:20px;padding:0px;width:auto;height:auto;border:1px solid black}.chat_box.svelte-auy6qm *{-ms-overflow-style:none;scrollbar-width:none}.chat_box.svelte-auy6qm *::-webkit-scrollbar{display:none}.chat_box.svelte-auy6qm .grid_box{display:grid;margin:5px;gap:5px;width:300px;height:400px}.chat_box.svelte-auy6qm .grid_box *{display:flex;flex-direction:column;position:relative;box-sizing:border-box}.chat_box.svelte-auy6qm .grid_box p{padding:10px;font-size:15px}.chat_box.svelte-auy6qm .panel{overflow-y:scroll}.chat_box.svelte-auy6qm .panel > *{margin-top:10px;margin-bottom:10px}.chat_box.svelte-auy6qm .__show_if_only_child{display:none}.chat_box.svelte-auy6qm .__show_if_only_child:only-child{display:flex;color:rgb(100, 100, 100)}.chat_box.svelte-auy6qm .__center{margin-left:auto;margin-right:auto}.chat_box.svelte-auy6qm .__border_top{border-top:1px solid black}.chat_box.svelte-auy6qm .__check_change_next:checked ~ .__to_show{display:flex}.chat_box.svelte-auy6qm .__check_change_next:checked ~ .__to_block,.chat_box.svelte-auy6qm .__check_change_next:checked ~ .__to_block *{pointer-events:none;color:rgb(100, 100, 100)}.chat_box.svelte-auy6qm .__to_show{display:none}.grid_box.svelte-fc4a40 .chat{grid-area:chat}.grid_box.svelte-fc4a40{gap:0px;grid:' chat ' auto
|
||||
/ auto }.chat_box.close .grid_box.svelte-fc4a40{margin:0px;width:auto;height:auto}.chat_box.svelte-auy6qm{display:flex;position:fixed;bottom:20px;right:20px;padding:0px;width:auto;height:auto;border:1px solid black}.chat_box.svelte-auy6qm *{-ms-overflow-style:none;scrollbar-width:none}.chat_box.svelte-auy6qm *::-webkit-scrollbar{display:none}.chat_box.svelte-auy6qm .grid_box{display:grid;margin:5px;gap:5px;width:300px;height:400px}.chat_box.svelte-auy6qm .grid_box *{display:flex;flex-direction:column;position:relative;box-sizing:border-box}.chat_box.svelte-auy6qm .grid_box p{padding:10px;font-size:15px}.chat_box.svelte-auy6qm .panel{overflow-y:scroll}.chat_box.svelte-auy6qm .panel > *{margin-top:10px;margin-bottom:10px}.chat_box.svelte-auy6qm .__show_if_only_child{display:none}.chat_box.svelte-auy6qm .__show_if_only_child:only-child{display:flex;color:rgb(100, 100, 100)}.chat_box.svelte-auy6qm .__center{margin-left:auto;margin-right:auto}.chat_box.svelte-auy6qm .__border_top{border-top:1px solid black}.chat_box.svelte-auy6qm .__check_change_next:checked ~ .__to_show{display:flex}.chat_box.svelte-auy6qm .__check_change_next:checked ~ .__to_block,.chat_box.svelte-auy6qm .__check_change_next:checked ~ .__to_block *{pointer-events:none;color:rgb(100, 100, 100)}.chat_box.svelte-auy6qm .__to_show{display:none}
|
||||
/ auto 1fr auto }.panel_user.svelte-1fj8iha{margin-top:-5px}button.svelte-j5z24r.svelte-j5z24r{display:flex;padding:0px;margin:auto;width:100%;cursor:pointer;outline:none;border:none;background-color:rgb(220, 220, 220)}button.svelte-j5z24r p.svelte-j5z24r{width:100%;margin:auto;text-align:center}button.svelte-j5z24r.svelte-j5z24r:hover{background-color:rgb(200, 200, 200)}button.svelte-j5z24r.svelte-j5z24r:active{background-color:rgb(190, 190, 190)}.list.svelte-j5z24r.svelte-j5z24r:not(:hover){background-color:rgb(240, 240, 240)}.list.svelte-j5z24r p.svelte-j5z24r{text-align:left}.transparent.svelte-j5z24r.svelte-j5z24r:not(:hover){background-color:transparent}.deactivate.svelte-j5z24r.svelte-j5z24r{background-color:transparent;pointer-events:none}.icon.svelte-j5z24r p.svelte-j5z24r{display:none}.icon.svelte-j5z24r.svelte-j5z24r:not(:hover){background-color:transparent}.icon.svelte-j5z24r.svelte-j5z24r{width:30px;height:100%;padding:0px}.dots.svelte-j5z24r.svelte-j5z24r::after{content:'\2807';font-size:20px;position:absolute;top:50%;left:0px;width:100%;height:auto;text-align:center;transform:translateY(-50%);cursor:pointer}.close.svelte-j5z24r.svelte-j5z24r::before{content:"";position:absolute;top:calc(50% - 1px);left:5px;width:20px;height:2px;background-color:black}.back.svelte-j5z24r.svelte-j5z24r::before{content:"";position:absolute;top:calc(50% - 6px - 1px);left:6px;width:14px;height:14px;border-left:1px solid black;border-bottom:1px solid black;transform:rotate(45deg)}.blocked.svelte-j5z24r.svelte-j5z24r{padding-left:30px}.blocked.svelte-j5z24r.svelte-j5z24r::before{content:"";position:absolute;top:calc(50% - 2px);left:10px;cursor:pointer;width:13px;height:10px;border-radius:2px;background-color:rgb(110, 110, 110)}.blocked.svelte-j5z24r.svelte-j5z24r::after{content:"";position:absolute;top:calc(50% - 9px);left:12px;cursor:pointer;width:9px;height:13px;border-radius:5px;box-sizing:border-box;border:3px solid rgb(110, 110, 110)}.chat_msg.svelte-14xxpbz.svelte-14xxpbz{margin:5px auto;padding:5px;border-radius:5px}.chat_msg.svelte-14xxpbz.svelte-14xxpbz{margin-left:0px;background-color:rgb(210, 210, 210);max-width:80%}.chat_msg.svelte-14xxpbz p.svelte-14xxpbz{padding:0px}.chat_msg.svelte-14xxpbz p.name.svelte-14xxpbz{margin:0px;font-size:12px;color:rgb(100, 100, 100)}.chat_msg.svelte-14xxpbz p.msg.svelte-14xxpbz{margin:5px 0px}.chat_msg.svelte-14xxpbz p.msg.svelte-14xxpbz *{display:inline}.chat_msg.me.svelte-14xxpbz.svelte-14xxpbz{margin-right:0px;margin-left:auto;background-color:rgb(210, 110, 10)}.chat_msg.me.svelte-14xxpbz p.name.svelte-14xxpbz{display:none}.chat_msg.SERVER.svelte-14xxpbz.svelte-14xxpbz{margin-left:auto;background-color:transparent}.chat_msg.SERVER.svelte-14xxpbz p.name.svelte-14xxpbz{display:none}.chat_msg.SERVER.svelte-14xxpbz p.msg.svelte-14xxpbz{margin:0px auto;font-size:12px;color:rgb(100, 100, 100)}@font-face{font-family:"Bit5x3";src:url("/fonts/Bit5x3.woff2") format("woff2"),
|
||||
local("Bit5x3"),
|
||||
url("/fonts/Bit5x3.woff") format("woff");font-weight:normal;font-style:normal;font-display:swap}#game_page.svelte-y455cj.svelte-y455cj{margin:0;background-color:#222425;position:relative;width:100%;height:100%}#canvas_container.svelte-y455cj.svelte-y455cj{margin-top:20px;text-align:center}#users_name.svelte-y455cj.svelte-y455cj{text-align:center;font-family:"Bit5x3";color:rgb(245, 245, 245);font-size:x-large}#div_game.svelte-y455cj.svelte-y455cj{margin-top:20px;text-align:center;font-family:"Bit5x3";color:rgb(245, 245, 245);font-size:x-large}#error_notification.svelte-y455cj.svelte-y455cj{text-align:center;display:block;font-family:"Bit5x3";color:rgb(143, 19, 19);font-size:x-large}#div_game.svelte-y455cj fieldset.svelte-y455cj{max-width:50vw;width:auto;margin:0 auto}#div_game.svelte-y455cj fieldset div.svelte-y455cj{padding:10px}#pong_button.svelte-y455cj.svelte-y455cj{font-family:"Bit5x3";color:rgb(245, 245, 245);background-color:#333333;font-size:x-large;padding:10px}canvas.svelte-y455cj.svelte-y455cj{background-color:#333333;max-width:75vw;width:80%}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
BIN
srcs/requirements/svelte/api_front/public/favicon.ico
Normal file
BIN
srcs/requirements/svelte/api_front/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.1 KiB |
@@ -1,107 +0,0 @@
|
||||
|
||||
import * as en from "../enums.js"
|
||||
|
||||
/* From Server */
|
||||
class ServerEvent {
|
||||
type: en.EventTypes;
|
||||
constructor(type: en.EventTypes = 0) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
class EventAssignId extends ServerEvent {
|
||||
id: string;
|
||||
constructor(id: string) {
|
||||
super(en.EventTypes.assignId);
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
class EventMatchmakingComplete extends ServerEvent {
|
||||
side: en.PlayerSide;
|
||||
constructor(side: en.PlayerSide) {
|
||||
super(en.EventTypes.matchmakingComplete);
|
||||
this.side = side;
|
||||
}
|
||||
}
|
||||
|
||||
class EventGameUpdate extends ServerEvent {
|
||||
playerLeft = {
|
||||
y: 0
|
||||
};
|
||||
playerRight = {
|
||||
y: 0
|
||||
};
|
||||
ballsArr: {
|
||||
x: number,
|
||||
y: number,
|
||||
dirX: number,
|
||||
dirY: number,
|
||||
speed: number
|
||||
}[] = [];
|
||||
wallTop? = {
|
||||
y: 0
|
||||
};
|
||||
wallBottom? = {
|
||||
y: 0
|
||||
};
|
||||
lastInputId = 0;
|
||||
constructor() { // TODO: constructor that take GameComponentsServer maybe ?
|
||||
super(en.EventTypes.gameUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
class EventScoreUpdate extends ServerEvent {
|
||||
scoreLeft: number;
|
||||
scoreRight: number;
|
||||
constructor(scoreLeft: number, scoreRight: number) {
|
||||
super(en.EventTypes.scoreUpdate);
|
||||
this.scoreLeft = scoreLeft;
|
||||
this.scoreRight = scoreRight;
|
||||
}
|
||||
}
|
||||
|
||||
class EventMatchEnd extends ServerEvent {
|
||||
winner: en.PlayerSide;
|
||||
constructor(winner: en.PlayerSide) {
|
||||
super(en.EventTypes.matchEnd);
|
||||
this.winner = winner;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* From Client */
|
||||
class ClientEvent {
|
||||
type: en.EventTypes; // readonly ?
|
||||
constructor(type: en.EventTypes = 0) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
class ClientAnnounce extends ClientEvent {
|
||||
role: en.ClientRole;
|
||||
clientId: string;
|
||||
matchOptions: en.MatchOptions;
|
||||
constructor(role: en.ClientRole, matchOptions: en.MatchOptions, clientId: string = "") {
|
||||
super(en.EventTypes.clientAnnounce);
|
||||
this.role = role;
|
||||
this.clientId = clientId;
|
||||
this.matchOptions = matchOptions;
|
||||
}
|
||||
}
|
||||
|
||||
class EventInput extends ClientEvent {
|
||||
input: en.InputEnum;
|
||||
id: number;
|
||||
constructor(input: en.InputEnum = en.InputEnum.noInput, id: number = 0) {
|
||||
super(en.EventTypes.clientInput);
|
||||
this.input = input;
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
ServerEvent, EventAssignId, EventMatchmakingComplete,
|
||||
EventGameUpdate, EventScoreUpdate, EventMatchEnd,
|
||||
ClientEvent, ClientAnnounce, EventInput
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
|
||||
import * as c from "./constants.js";
|
||||
import * as en from "../shared_js/enums.js"
|
||||
import { gc, matchOptions, clientInfo } from "./global.js";
|
||||
import { wallsMovements } from "../shared_js/wallsMovement.js";
|
||||
|
||||
let actual_time: number = Date.now();
|
||||
let last_time: number;
|
||||
let delta_time: number;
|
||||
|
||||
function gameLoop()
|
||||
{
|
||||
/* last_time = actual_time;
|
||||
actual_time = Date.now();
|
||||
delta_time = (actual_time - last_time) / 1000; */
|
||||
|
||||
delta_time = c.fixedDeltaTime;
|
||||
// console.log(`delta_gameLoop: ${delta_time}`);
|
||||
|
||||
// interpolation
|
||||
// console.log(`dir.y: ${clientInfo.opponent.dir.y}, pos.y: ${clientInfo.opponent.pos.y}, opponentNextPos.y: ${clientInfo.opponentNextPos.y}`);
|
||||
if (clientInfo.opponent.dir.y != 0 ) {
|
||||
opponentInterpolation(delta_time);
|
||||
}
|
||||
|
||||
// client prediction
|
||||
gc.ballsArr.forEach((ball) => {
|
||||
ball.moveAndBounce(delta_time, [gc.wallTop, gc.wallBottom, gc.playerLeft, gc.playerRight]);
|
||||
});
|
||||
|
||||
if (matchOptions & en.MatchOptions.movingWalls) {
|
||||
wallsMovements(delta_time, gc);
|
||||
}
|
||||
}
|
||||
|
||||
function opponentInterpolation(delta: number)
|
||||
{
|
||||
// interpolation
|
||||
clientInfo.opponent.moveAndCollide(delta, [gc.wallTop, gc.wallBottom]);
|
||||
|
||||
if ((clientInfo.opponent.dir.y > 0 && clientInfo.opponent.pos.y > clientInfo.opponentNextPos.y)
|
||||
|| (clientInfo.opponent.dir.y < 0 && clientInfo.opponent.pos.y < clientInfo.opponentNextPos.y))
|
||||
{
|
||||
clientInfo.opponent.dir.y = 0;
|
||||
clientInfo.opponent.pos.y = clientInfo.opponentNextPos.y;
|
||||
}
|
||||
}
|
||||
|
||||
export {gameLoop}
|
||||
@@ -1,3 +0,0 @@
|
||||
|
||||
export {pong, gc, matchOptions} from "./pong.js"
|
||||
export {socket, clientInfo} from "./ws.js"
|
||||
@@ -1,99 +0,0 @@
|
||||
|
||||
initDom();
|
||||
function initDom() {
|
||||
document.getElementById("play_pong_button").addEventListener("click", init);
|
||||
}
|
||||
|
||||
import * as c from "./constants.js"
|
||||
import * as en from "../shared_js/enums.js"
|
||||
import { GameArea } from "./class/GameArea.js";
|
||||
import { GameComponentsClient } from "./class/GameComponentsClient.js";
|
||||
import { handleInput } from "./handleInput.js";
|
||||
// import { sendLoop } from "./handleInput.js";
|
||||
import { gameLoop } from "./gameLoop.js"
|
||||
import { drawLoop } from "./draw.js";
|
||||
import { countdown } from "./utils.js";
|
||||
import { initWebSocket } from "./ws.js";
|
||||
import { initAudio } from "./audio.js";
|
||||
|
||||
|
||||
/* Keys
|
||||
Racket: W/S OR Up/Down
|
||||
Grid On-Off: G
|
||||
*/
|
||||
|
||||
/* TODO: A way to delay the init of variables, but still use "const" not "let" ? */
|
||||
export let pong: GameArea;
|
||||
export let gc: GameComponentsClient;
|
||||
export let matchOptions: en.MatchOptions = en.MatchOptions.noOption;
|
||||
|
||||
function init()
|
||||
{
|
||||
console.log("multi_balls:"+(<HTMLInputElement>document.getElementById("multi_balls")).checked);
|
||||
console.log("moving_walls:"+(<HTMLInputElement>document.getElementById("moving_walls")).checked);
|
||||
console.log("sound_on:"+(<HTMLInputElement>document.getElementById("sound_on")).checked);
|
||||
|
||||
let soundMutedFlag = false;
|
||||
if ( (<HTMLInputElement>document.getElementById("sound_off")).checked ) {
|
||||
soundMutedFlag = true;
|
||||
}
|
||||
initAudio(soundMutedFlag);
|
||||
|
||||
if ( (<HTMLInputElement>document.getElementById("multi_balls")).checked ) {
|
||||
matchOptions |= en.MatchOptions.multiBalls;
|
||||
}
|
||||
if ( (<HTMLInputElement>document.getElementById("moving_walls")).checked ) {
|
||||
matchOptions |= en.MatchOptions.movingWalls;
|
||||
}
|
||||
|
||||
document.getElementById("div_game_options").hidden = true;
|
||||
|
||||
pong = new GameArea();
|
||||
gc = new GameComponentsClient(matchOptions, pong.ctx);
|
||||
initWebSocket(matchOptions);
|
||||
}
|
||||
|
||||
function matchmaking()
|
||||
{
|
||||
console.log("Searching an opponent...");
|
||||
gc.text1.clear();
|
||||
gc.text1.pos.assign(c.w/5, c.h_mid);
|
||||
gc.text1.text = "Searching...";
|
||||
gc.text1.update();
|
||||
}
|
||||
|
||||
function matchmakingComplete()
|
||||
{
|
||||
console.log("Match Found !");
|
||||
gc.text1.clear();
|
||||
gc.text1.pos.assign(c.w/8, c.h_mid);
|
||||
gc.text1.text = "Match Found !";
|
||||
gc.text1.update();
|
||||
}
|
||||
|
||||
function startGame() {
|
||||
gc.text1.pos.assign(c.w_mid, c.h_mid+c.h/4);
|
||||
countdown(c.matchStartDelay/1000, (count: number) => {
|
||||
gc.text1.clear();
|
||||
gc.text1.text = `${count}`;
|
||||
gc.text1.update();
|
||||
}, resumeGame);
|
||||
}
|
||||
|
||||
function resumeGame()
|
||||
{
|
||||
gc.text1.text = "";
|
||||
window.addEventListener('keydown', function (e) {
|
||||
pong.addKey(e.key);
|
||||
});
|
||||
window.addEventListener('keyup', function (e) {
|
||||
pong.deleteKey(e.key);
|
||||
});
|
||||
pong.handleInputInterval = window.setInterval(handleInput, c.handleInputIntervalMS);
|
||||
// pong.handleInputInterval = window.setInterval(sendLoop, c.sendLoopIntervalMS);
|
||||
pong.gameLoopInterval = window.setInterval(gameLoop, c.gameLoopIntervalMS);
|
||||
pong.drawLoopInterval = window.setInterval(drawLoop, c.drawLoopIntervalMS);
|
||||
}
|
||||
|
||||
|
||||
export {matchmaking, matchmakingComplete, startGame}
|
||||
@@ -1,27 +0,0 @@
|
||||
|
||||
import { MovingRectangle } from "./class/Rectangle.js";
|
||||
|
||||
function random(min: number = 0, max: number = 1) {
|
||||
return Math.random() * (max - min) + min;
|
||||
}
|
||||
|
||||
function sleep (ms: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
function clamp(n: number, min: number, max: number) : number
|
||||
{
|
||||
if (n < min)
|
||||
n = min;
|
||||
else if (n > max)
|
||||
n = max;
|
||||
return (n);
|
||||
}
|
||||
|
||||
// Typescript hack, unused
|
||||
function assertMovingRectangle(value: unknown): asserts value is MovingRectangle {
|
||||
// if (value !== MovingRectangle) throw new Error("Not a MovingRectangle");
|
||||
return;
|
||||
}
|
||||
|
||||
export {random, sleep, clamp, assertMovingRectangle}
|
||||
@@ -4,9 +4,9 @@
|
||||
<meta charset='utf-8'>
|
||||
<meta name='viewport' content='width=device-width,initial-scale=1'>
|
||||
|
||||
<title>Svelte app</title>
|
||||
<title>Potato Pong</title>
|
||||
|
||||
<link rel='icon' type='image/png' href='/favicon.png'>
|
||||
<link rel='icon' type='image/x-icon' href='/favicon.ico'>
|
||||
<link rel='stylesheet' href='/global.css'>
|
||||
<link rel='stylesheet' href='/build/bundle.css'>
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -2,7 +2,7 @@
|
||||
// routing
|
||||
// may not need {link} here
|
||||
import Router, { link, replace } from "svelte-spa-router";
|
||||
import { primaryRoutes } from "./routes/primaryRoutes.js";
|
||||
import { primaryRoutes } from "./routes/primaryRoutes.js";
|
||||
// import primaryRoutes from "./routes/primaryRoutes.svelte";
|
||||
|
||||
const conditionsFailed = (event) => {
|
||||
|
||||
9
srcs/requirements/svelte/api_front/src/main.js
Normal file
9
srcs/requirements/svelte/api_front/src/main.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import App from './App.svelte';
|
||||
const app = new App({
|
||||
target: document.body,
|
||||
props: {
|
||||
// name: 'world'
|
||||
}
|
||||
});
|
||||
export default app;
|
||||
//# sourceMappingURL=main.js.map
|
||||
1
srcs/requirements/svelte/api_front/src/main.js.map
Normal file
1
srcs/requirements/svelte/api_front/src/main.js.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"main.js","sourceRoot":"","sources":["main.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,cAAc,CAAC;AAE/B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC;IACnB,MAAM,EAAE,QAAQ,CAAC,IAAI;IACrB,KAAK,EAAE;IACN,gBAAgB;KAChB;CACD,CAAC,CAAC;AAEH,eAAe,GAAG,CAAC"}
|
||||
@@ -1,106 +0,0 @@
|
||||
<script lang="ts">
|
||||
|
||||
// import { initDom } from "../game/client/pong.js";
|
||||
import {onMount} from 'svelte';
|
||||
|
||||
onMount(() => {
|
||||
// initDom();
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<body>
|
||||
<div id="div_game_options">
|
||||
<fieldset>
|
||||
<legend>game options</legend>
|
||||
<div>
|
||||
<input type="checkbox" id="multi_balls" name="multi_balls">
|
||||
<label for="multi_balls">multiples balls</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="moving_walls" name="moving_walls">
|
||||
<label for="moving_walls">moving walls</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>sound :</label>
|
||||
<input type="radio" id="sound_on" name="sound_selector" checked>
|
||||
<label for="sound_on">on</label>
|
||||
<input type="radio" id="sound_off" name="sound_selector">
|
||||
<label for="sound_off">off</label>
|
||||
</div>
|
||||
<div>
|
||||
<button id="play_pong_button">PLAY</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div id="div_game_instructions">
|
||||
<h2>--- keys ---</h2>
|
||||
<p>move up: 'w' or 'up arrow'</p>
|
||||
<p>move down: 's' OR 'down arrow'</p>
|
||||
<p>grid on/off: 'g'</p>
|
||||
</div>
|
||||
|
||||
<div id="canvas_container">
|
||||
<!-- <p> =) </p> -->
|
||||
</div>
|
||||
|
||||
<!-- <script src="http://localhost:8080/js/pong.js" type="module" defer></script> -->
|
||||
</body>
|
||||
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: "Bit5x3";
|
||||
src: url("/fonts/Bit5x3.woff2") format("woff2"),
|
||||
url("/fonts/Bit5x3.woff") format("woff");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
background-color: #222425;
|
||||
}
|
||||
#canvas_container {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
/* border: dashed rgb(245, 245, 245) 5px; */
|
||||
/* max-height: 80vh; */
|
||||
/* overflow: hidden; */
|
||||
}
|
||||
#div_game_instructions {
|
||||
text-align: center;
|
||||
font-family: "Bit5x3";
|
||||
color: rgb(245, 245, 245);
|
||||
font-size: large;
|
||||
}
|
||||
#div_game_options {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
font-family: "Bit5x3";
|
||||
color: rgb(245, 245, 245);
|
||||
font-size: x-large;
|
||||
}
|
||||
#div_game_options fieldset {
|
||||
max-width: 50vw;
|
||||
width: auto;
|
||||
margin: 0 auto;
|
||||
}
|
||||
#div_game_options fieldset div {
|
||||
padding: 10px;
|
||||
}
|
||||
#play_pong_button {
|
||||
font-family: "Bit5x3";
|
||||
color: rgb(245, 245, 245);
|
||||
background-color: #333333;
|
||||
font-size: x-large;
|
||||
padding: 10px;
|
||||
}
|
||||
canvas {
|
||||
background-color: #333333;
|
||||
max-width: 75vw;
|
||||
/* max-height: 100vh; */
|
||||
width: 80%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,35 @@
|
||||
<script lang="ts">
|
||||
import Header from '../pieces/Header.svelte';
|
||||
import MatchListElem from "../pieces/MatchListElem.svelte";
|
||||
|
||||
let arr = [
|
||||
{
|
||||
id: "match-01",
|
||||
playerOneUsername: "toto",
|
||||
playerTwoUsername: "bruno",
|
||||
},
|
||||
{
|
||||
id: "match-02",
|
||||
playerOneUsername: "bertand",
|
||||
playerTwoUsername: "cassandre",
|
||||
},
|
||||
{
|
||||
id: "match-03",
|
||||
playerOneUsername: "madeleine",
|
||||
playerTwoUsername: "jack",
|
||||
},
|
||||
];
|
||||
</script>
|
||||
<!-- -->
|
||||
|
||||
<Header/>
|
||||
<menu>
|
||||
{#each arr as match}
|
||||
<MatchListElem match={match}/>
|
||||
{/each}
|
||||
</menu>
|
||||
|
||||
|
||||
<!-- -->
|
||||
<style>
|
||||
</style>
|
||||
@@ -1,88 +1,381 @@
|
||||
|
||||
<script>
|
||||
import "public/game/pong.js"
|
||||
<script lang="ts">
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
import Header from '../../pieces/Header.svelte';
|
||||
import { fade, fly } from 'svelte/transition';
|
||||
|
||||
import * as pong from "./client/pong";
|
||||
|
||||
// Pour Chérif: variables indiquant l'état du match
|
||||
import { matchEnded, matchAbort } from "./client/ws";
|
||||
|
||||
//user's stuff
|
||||
let user;
|
||||
let allUsers;
|
||||
|
||||
//Game's stuff
|
||||
let optionsAreNotSet = true;
|
||||
const options = new pong.InitOptions();
|
||||
|
||||
//Game's stuff client side only
|
||||
const gameAreaId = "game_area";
|
||||
|
||||
//html boolean for pages
|
||||
let showWaitPage = false;
|
||||
let showInvitations = false;
|
||||
let showGameOption = true;
|
||||
let showError = false;
|
||||
let hiddenGame = true;
|
||||
let showMatchEnded = false;
|
||||
|
||||
let isThereAnyInvitation = false;
|
||||
let invitations = [];
|
||||
|
||||
let waitingMessage = "Please wait..."
|
||||
let errorMessageWhenAttemptingToGetATicket = "";
|
||||
let idOfIntevalCheckTerminationOfTheMatch;
|
||||
|
||||
onMount( async() => {
|
||||
user = await fetch('http://transcendance:8080/api/v2/user')
|
||||
.then( x => x.json() );
|
||||
allUsers = await fetch('http://transcendance:8080/api/v2/user/all')
|
||||
.then( x => x.json() );
|
||||
options.playerOneUsername = user.username;
|
||||
})
|
||||
|
||||
onDestroy( async() => {
|
||||
options.playerOneUsername = user.username;
|
||||
showError = false;
|
||||
showMatchEnded = false;
|
||||
optionsAreNotSet = true
|
||||
options.playerTwoUsername = "";
|
||||
options.isSomeoneIsInvited = false;
|
||||
options.isInvitedPerson = false;
|
||||
options.moving_walls = false;
|
||||
options.multi_balls = false;
|
||||
errorMessageWhenAttemptingToGetATicket = "";
|
||||
hiddenGame = true;
|
||||
isThereAnyInvitation = false;
|
||||
invitations = [];
|
||||
pong.destroy();
|
||||
clearInterval(idOfIntevalCheckTerminationOfTheMatch);
|
||||
})
|
||||
|
||||
const initGame = async() =>
|
||||
{
|
||||
optionsAreNotSet = false;
|
||||
showWaitPage = true;
|
||||
idOfIntevalCheckTerminationOfTheMatch = setInterval(matchTermitation, 1000);
|
||||
const matchOptions = pong.computeMatchOptions(options);
|
||||
|
||||
const responseWhenGrantToken = fetch("http://transcendance:8080/api/v2/game/ticket", {
|
||||
method : "POST",
|
||||
headers : {'Content-Type': 'application/json'},
|
||||
body : JSON.stringify({
|
||||
playerOneUsername : options.playerOneUsername,
|
||||
playerTwoUsername : options.playerTwoUsername,
|
||||
gameOptions : matchOptions,
|
||||
isGameIsWithInvitation : options.isSomeoneIsInvited
|
||||
})
|
||||
})
|
||||
const responseFromServer = await responseWhenGrantToken;
|
||||
const responseInjson = await responseFromServer.json();
|
||||
const token : string = responseInjson.token;
|
||||
showWaitPage = false;
|
||||
console.log("status : " + responseFromServer.status)
|
||||
if (responseFromServer.status != 200)
|
||||
{
|
||||
console.log(responseInjson)
|
||||
console.log("On refuse le ticket");
|
||||
clearInterval(idOfIntevalCheckTerminationOfTheMatch);
|
||||
errorMessageWhenAttemptingToGetATicket = responseInjson.message;
|
||||
showError = true;
|
||||
options.playerTwoUsername = "";
|
||||
options.playerOneUsername = user.username;
|
||||
options.isSomeoneIsInvited = false;
|
||||
options.isInvitedPerson = false;
|
||||
options.moving_walls = false;
|
||||
options.multi_balls = false;
|
||||
setTimeout(() => {
|
||||
showError = false;
|
||||
showWaitPage = false
|
||||
optionsAreNotSet = true
|
||||
|
||||
}, 5000);
|
||||
}
|
||||
else if (token)
|
||||
{
|
||||
options.isInvitedPerson = false
|
||||
pong.init(options, gameAreaId, token);
|
||||
hiddenGame = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Pour Cherif: renommer en un truc du genre "initGameForInvitedPlayer" ?
|
||||
const initGameForPrivateParty = async(invitation : any) =>
|
||||
{
|
||||
idOfIntevalCheckTerminationOfTheMatch = setInterval(matchTermitation, 1000);
|
||||
optionsAreNotSet = false
|
||||
showWaitPage = true
|
||||
console.log("invitation : ")
|
||||
console.log(invitation)
|
||||
if (invitation.token)
|
||||
{
|
||||
options.playerOneUsername = invitation.playerOneUsername;
|
||||
options.playerTwoUsername = invitation.playerTwoUsername;
|
||||
options.isSomeoneIsInvited = true;
|
||||
options.isInvitedPerson = true
|
||||
pong.init(options, gameAreaId, invitation.token);
|
||||
showWaitPage = false
|
||||
hiddenGame = false;
|
||||
}
|
||||
}
|
||||
|
||||
const matchTermitation = () => {
|
||||
console.log("Ping matchTermitation")
|
||||
if (matchAbort || matchEnded)
|
||||
{
|
||||
clearInterval(idOfIntevalCheckTerminationOfTheMatch);
|
||||
console.log("matchTermitation was called")
|
||||
showWaitPage = false
|
||||
matchAbort ?
|
||||
errorMessageWhenAttemptingToGetATicket = "The match has been aborted"
|
||||
: errorMessageWhenAttemptingToGetATicket = "The match is finished !"
|
||||
matchAbort ? showError = true : showMatchEnded = true;
|
||||
setTimeout(() => {
|
||||
hiddenGame = true;
|
||||
showError = false;
|
||||
showMatchEnded = false;
|
||||
optionsAreNotSet = true
|
||||
options.playerTwoUsername = "";
|
||||
options.playerOneUsername = user.username;
|
||||
options.isSomeoneIsInvited = false;
|
||||
options.isInvitedPerson = false;
|
||||
options.moving_walls = false;
|
||||
options.multi_balls = false;
|
||||
errorMessageWhenAttemptingToGetATicket = "";
|
||||
options.playerTwoUsername = "";
|
||||
hiddenGame = true;
|
||||
isThereAnyInvitation = false;
|
||||
invitations = [];
|
||||
pong.destroy();
|
||||
console.log("matchTermitation : setTimeout")
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const showOptions = () => {
|
||||
showGameOption = true
|
||||
showInvitations = false
|
||||
}
|
||||
|
||||
const showInvitation = async() => {
|
||||
showGameOption = false;
|
||||
showInvitations = true;
|
||||
invitations = await fetch("http://transcendance:8080/api/v2/game/invitations")
|
||||
.then(x => x.json())
|
||||
invitations.length !== 0 ? isThereAnyInvitation = true : isThereAnyInvitation = false
|
||||
}
|
||||
|
||||
const rejectInvitation = async(invitation) => {
|
||||
await fetch("http://transcendance:8080/api/v2/game/decline",{
|
||||
method: "POST",
|
||||
headers: { 'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
token : invitation.token
|
||||
})
|
||||
})
|
||||
.then(x => x.json())
|
||||
.catch(error => console.log(error))
|
||||
showInvitation()
|
||||
}
|
||||
|
||||
const acceptInvitation = async(invitation : any) => {
|
||||
const res = await fetch("http://transcendance:8080/api/v2/game/accept",{
|
||||
method: "POST",
|
||||
headers: { 'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
token : invitation.token
|
||||
})
|
||||
})
|
||||
.then(x => x.json())
|
||||
.catch(error => {
|
||||
console.log(error)
|
||||
})
|
||||
if (res.status === 200)
|
||||
{
|
||||
showInvitation()
|
||||
initGameForPrivateParty(invitation)
|
||||
}
|
||||
//Au final c'est utile !
|
||||
|
||||
initGameForPrivateParty(invitation)
|
||||
}
|
||||
</script>
|
||||
|
||||
<Header />
|
||||
<!-- <div id="game_page"> Replacement for <body>.
|
||||
Might become useless after CSS rework. -->
|
||||
<div id="game_page">
|
||||
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="preload_font">.</div>
|
||||
|
||||
<div id="div_game_options">
|
||||
{#if showMatchEnded === true}
|
||||
<div id="div_game" in:fly="{{ y: 10, duration: 1000 }}">
|
||||
<p>{errorMessageWhenAttemptingToGetATicket}</p>
|
||||
</div>
|
||||
{/if}
|
||||
{#if showError === true}
|
||||
<div id="div_game" in:fly="{{ y: 10, duration: 1000 }}">
|
||||
<fieldset>
|
||||
<legend>game options</legend>
|
||||
<div>
|
||||
<input type="checkbox" id="multi_balls" name="multi_balls">
|
||||
<label for="multi_balls">multiples balls</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="moving_walls" name="moving_walls">
|
||||
<label for="moving_walls">moving walls</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>sound :</label>
|
||||
<input type="radio" id="sound_on" name="sound_selector" checked>
|
||||
<label for="sound_on">on</label>
|
||||
<input type="radio" id="sound_off" name="sound_selector">
|
||||
<label for="sound_off">off</label>
|
||||
</div>
|
||||
<div>
|
||||
<button id="play_pong_button">PLAY</button>
|
||||
</div>
|
||||
<legend>Error</legend>
|
||||
<p>{errorMessageWhenAttemptingToGetATicket}</p>
|
||||
</fieldset>
|
||||
</div>
|
||||
{/if}
|
||||
<div id="canvas_container" hidden={hiddenGame}>
|
||||
<canvas id={gameAreaId}/>
|
||||
</div>
|
||||
|
||||
<div id="canvas_container">
|
||||
<!-- <p> =) </p> -->
|
||||
</div>
|
||||
{#if showWaitPage === true}
|
||||
<div id="div_game" in:fly="{{ y: 10, duration: 1000 }}">
|
||||
<fieldset>
|
||||
<legend>Connecting to the game...</legend>
|
||||
<p>{waitingMessage}</p>
|
||||
</fieldset>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<script src="public/game/pong.js" type="module" defer></script>
|
||||
</body>
|
||||
|
||||
|
||||
{#if optionsAreNotSet}
|
||||
{#if showGameOption === true}
|
||||
<div id="game_option">
|
||||
<form on:submit|preventDefault={() => initGame()}>
|
||||
<div id="div_game">
|
||||
<button id="pong_button" on:click={showInvitation}>Show invitations</button>
|
||||
<fieldset>
|
||||
<legend>game options</legend>
|
||||
<div>
|
||||
<input type="checkbox" id="multi_balls" name="multi_balls" bind:checked={options.multi_balls}>
|
||||
<label for="multi_balls">Multiples balls</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="moving_walls" name="moving_walls" bind:checked={options.moving_walls}>
|
||||
<label for="moving_walls">Moving walls</label>
|
||||
</div>
|
||||
<div>
|
||||
<p>sound :</p>
|
||||
<input type="radio" id="sound_on" name="sound_selector" bind:group={options.sound} value="on">
|
||||
<label for="sound_on">on</label>
|
||||
<input type="radio" id="sound_off" name="sound_selector" bind:group={options.sound} value="off">
|
||||
<label for="sound_off">off</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="isSomeoneIsInvited" bind:checked={options.isSomeoneIsInvited}>
|
||||
<label for="moving_walls">Invite a friend</label>
|
||||
</div>
|
||||
{#if options.isSomeoneIsInvited === true}
|
||||
<select bind:value={options.playerTwoUsername}>
|
||||
{#each allUsers as user }
|
||||
<option value={user.username}>{user.username}</option>
|
||||
{/each}
|
||||
</select>
|
||||
{/if}
|
||||
<div>
|
||||
<button id="pong_button" >PLAY</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if showInvitations}
|
||||
<div id="invitations_options" in:fly="{{ y: 10, duration: 1000 }}">
|
||||
<div id="div_game">
|
||||
<button id="pong_button" on:click={showOptions}>Play a Game</button>
|
||||
<fieldset>
|
||||
<legend>Current invitation(s)</legend>
|
||||
{#if isThereAnyInvitation}
|
||||
{#each invitations as invitation }
|
||||
<div>
|
||||
{invitation.playerOneUsername} has invited you to play a pong !
|
||||
<button id="pong_button" on:click={() => acceptInvitation(invitation)}>V</button>
|
||||
<button id="pong_button" on:click={() => rejectInvitation(invitation)}>X</button>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
{#if isThereAnyInvitation === false}
|
||||
<p>Currently, no one asked to play with you.</p>
|
||||
<button id="pong_button" on:click={showInvitation}>Reload</button>
|
||||
{/if}
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
</div> <!-- div "game_page" -->
|
||||
|
||||
<style>
|
||||
|
||||
@font-face {
|
||||
font-family: "Bit5x3";
|
||||
src: url("/fonts/Bit5x3.woff2") format("woff2"),local("Bit5x3"), url("/fonts/Bit5x3.woff") format("woff");
|
||||
src:
|
||||
url("/fonts/Bit5x3.woff2") format("woff2"),
|
||||
local("Bit5x3"),
|
||||
url("/fonts/Bit5x3.woff") format("woff");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
#preload_font {
|
||||
font-family: "Bit5x3";
|
||||
opacity:0;
|
||||
height:0;
|
||||
width:0;
|
||||
display:inline-block;
|
||||
}
|
||||
body {
|
||||
#game_page {
|
||||
margin: 0;
|
||||
background-color: #222425;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
#canvas_container {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
/* border: dashed rgb(245, 245, 245) 5px; */
|
||||
/* max-height: 80vh; */
|
||||
/* overflow: hidden; */
|
||||
}
|
||||
#div_game_options {
|
||||
|
||||
#users_name {
|
||||
text-align: center;
|
||||
font-family: "Bit5x3";
|
||||
color: rgb(245, 245, 245);
|
||||
font-size: x-large;
|
||||
}
|
||||
#div_game_options fieldset {
|
||||
|
||||
#div_game {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
font-family: "Bit5x3";
|
||||
color: rgb(245, 245, 245);
|
||||
font-size: x-large;
|
||||
}
|
||||
|
||||
#error_notification {
|
||||
text-align: center;
|
||||
display: block;
|
||||
font-family: "Bit5x3";
|
||||
color: rgb(143, 19, 19);
|
||||
font-size: x-large;
|
||||
}
|
||||
|
||||
#div_game fieldset {
|
||||
max-width: 50vw;
|
||||
width: auto;
|
||||
margin: 0 auto;
|
||||
}
|
||||
#div_game_options fieldset div {
|
||||
#div_game fieldset div {
|
||||
padding: 10px;
|
||||
}
|
||||
#play_pong_button {
|
||||
#pong_button {
|
||||
font-family: "Bit5x3";
|
||||
color: rgb(245, 245, 245);
|
||||
background-color: #333333;
|
||||
@@ -90,6 +383,7 @@ body {
|
||||
padding: 10px;
|
||||
}
|
||||
canvas {
|
||||
/* background-color: #ff0000; */
|
||||
background-color: #333333;
|
||||
max-width: 75vw;
|
||||
/* max-height: 100vh; */
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
import Header from '../../pieces/Header.svelte';
|
||||
import { fade, fly } from 'svelte/transition';
|
||||
|
||||
import * as pongSpectator from "./client/pongSpectator";
|
||||
|
||||
// Pour Chérif: variables indiquant l'état du match
|
||||
import { matchEnded, matchAbort } from "./client/ws";
|
||||
|
||||
//user's stuff
|
||||
let user;
|
||||
let allUsers;
|
||||
|
||||
//Game's stuff client side only
|
||||
const gameAreaId = "game_area";
|
||||
|
||||
//html boolean for pages
|
||||
let hiddenGame = true;
|
||||
|
||||
onMount( async() => {
|
||||
user = await fetch('http://transcendance:8080/api/v2/user')
|
||||
.then( x => x.json() );
|
||||
allUsers = await fetch('http://transcendance:8080/api/v2/user/all')
|
||||
.then( x => x.json() );
|
||||
})
|
||||
|
||||
onDestroy( async() => {
|
||||
pongSpectator.destroy();
|
||||
})
|
||||
|
||||
const initGameSpectator = async() => {
|
||||
let gameSessionId = "ID_PLACEHOLDER"; // PLACEHOLDER
|
||||
let matchOptions = 0; // PLACEHOLDER
|
||||
let sound = "off"; // PLACEHOLDER
|
||||
pongSpectator.init(matchOptions, sound, gameAreaId, gameSessionId);
|
||||
hiddenGame = false;
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<Header />
|
||||
<!-- <div id="game_page"> Replacement for <body>.
|
||||
Might become useless after CSS rework. -->
|
||||
<div id="game_page">
|
||||
|
||||
<div id="canvas_container" hidden={hiddenGame}>
|
||||
<canvas id={gameAreaId}/>
|
||||
</div>
|
||||
|
||||
</div> <!-- div "game_page" -->
|
||||
|
||||
<style>
|
||||
</style>
|
||||
@@ -0,0 +1,73 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
import Header from "../../pieces/Header.svelte";
|
||||
|
||||
//user's stuff
|
||||
let currentUser;
|
||||
let allUsers = [];
|
||||
let idInterval;
|
||||
onMount( async() => {
|
||||
currentUser = await fetch('http://transcendance:8080/api/v2/user')
|
||||
.then( x => x.json() );
|
||||
allUsers = await fetch('http://transcendance:8080/api/v2/game/ranking')
|
||||
.then( x => x.json() );
|
||||
idInterval = setInterval(fetchScores, 10000);
|
||||
})
|
||||
|
||||
onDestroy( async() => {
|
||||
clearInterval(idInterval);
|
||||
})
|
||||
|
||||
function fetchScores() {
|
||||
fetch('http://transcendance:8080/api/v2/game/ranking')
|
||||
.then( x => x.json() )
|
||||
.then( x => allUsers = x );
|
||||
}
|
||||
</script>
|
||||
<Header />
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h1>Ranking</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Username</th>
|
||||
<th scope="col">Win</th>
|
||||
<th scope="col">Lose</th>
|
||||
<th scope="col">Draw</th>
|
||||
<th scope="col">Games Played</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each allUsers as user, i}
|
||||
<tr>
|
||||
<th scope="row">{i + 1}</th>
|
||||
{#if user.username === currentUser.username}
|
||||
<td><b>You ({user.username})</b></td>
|
||||
{:else}
|
||||
<td>{user.username}</td>
|
||||
{/if}
|
||||
<td>{user.stats.winGame}</td>
|
||||
<td>{user.stats.loseGame}</td>
|
||||
<td>{user.stats.drawGame}</td>
|
||||
<td>{user.stats.totalGame}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -2,12 +2,17 @@
|
||||
import * as c from "./constants.js"
|
||||
|
||||
export const soundPongArr: HTMLAudioElement[] = [];
|
||||
export const soundRoblox = new Audio("http://localhost:8080/sound/roblox-oof.ogg");
|
||||
export const soundRoblox = new Audio("http://transcendance:8080/sound/roblox-oof.ogg");
|
||||
|
||||
export function initAudio(muteFlag: boolean)
|
||||
export function initAudio(sound: string)
|
||||
{
|
||||
let muteFlag = true;
|
||||
if (sound === "on") {
|
||||
muteFlag = false;
|
||||
}
|
||||
|
||||
for (let i = 0; i <= 32; i++) {
|
||||
soundPongArr.push(new Audio("http://localhost:8080/sound/pong/"+i+".ogg"));
|
||||
soundPongArr.push(new Audio("http://transcendance:8080/sound/pong/"+i+".ogg"));
|
||||
soundPongArr[i].volume = c.soundPongVolume;
|
||||
soundPongArr[i].muted = muteFlag;
|
||||
}
|
||||
@@ -1,21 +1,25 @@
|
||||
|
||||
import * as c from ".././constants.js"
|
||||
|
||||
class GameArea {
|
||||
export class GameArea {
|
||||
keys: string[] = [];
|
||||
handleInputInterval: number = 0;
|
||||
gameLoopInterval: number = 0;
|
||||
drawLoopInterval: number = 0;
|
||||
canvas: HTMLCanvasElement;
|
||||
ctx: CanvasRenderingContext2D;
|
||||
constructor() {
|
||||
this.canvas = document.createElement("canvas");
|
||||
constructor(canvas_id: string) {
|
||||
const canvas = document.getElementById("game_area");
|
||||
if (canvas && canvas instanceof HTMLCanvasElement) {
|
||||
this.canvas = canvas;
|
||||
}
|
||||
else {
|
||||
console.log("GameArea init error, invalid canvas_id");
|
||||
return;
|
||||
}
|
||||
this.ctx = this.canvas.getContext("2d") as CanvasRenderingContext2D;
|
||||
this.canvas.width = c.CanvasWidth;
|
||||
this.canvas.height = c.CanvasWidth / c.CanvasRatio;
|
||||
let container = document.getElementById("canvas_container");
|
||||
if (container)
|
||||
container.insertBefore(this.canvas, container.childNodes[0]);
|
||||
}
|
||||
addKey(key: string) {
|
||||
key = key.toLowerCase();
|
||||
@@ -34,5 +38,3 @@ class GameArea {
|
||||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
}
|
||||
}
|
||||
|
||||
export {GameArea}
|
||||
@@ -5,7 +5,7 @@ import { Vector, VectorInteger } from "../../shared_js/class/Vector.js";
|
||||
import { TextElem, TextNumericValue } from "./Text.js";
|
||||
import { RectangleClient, MovingRectangleClient, RacketClient, BallClient, Line } from "./RectangleClient.js";
|
||||
import { GameComponents } from "../../shared_js/class/GameComponents.js";
|
||||
import { MovingRectangle } from "../../shared_js/class/Rectangle.js";
|
||||
import type { MovingRectangle } from "../../shared_js/class/Rectangle.js";
|
||||
|
||||
class GameComponentsExtensionForClient extends GameComponents {
|
||||
wallTop: RectangleClient | MovingRectangleClient;
|
||||
@@ -62,12 +62,13 @@ class GameComponentsExtensionForClient extends GameComponents {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class GameComponentsClient extends GameComponentsExtensionForClient {
|
||||
export class GameComponentsClient extends GameComponentsExtensionForClient {
|
||||
midLine: Line;
|
||||
scoreLeft: TextNumericValue;
|
||||
scoreRight: TextNumericValue;
|
||||
text1: TextElem;
|
||||
text2: TextElem;
|
||||
text3: TextElem;
|
||||
|
||||
w_grid_mid: RectangleClient;
|
||||
w_grid_u1: RectangleClient;
|
||||
@@ -90,6 +91,8 @@ class GameComponentsClient extends GameComponentsExtensionForClient {
|
||||
// Text
|
||||
pos.assign(0, c.h_mid);
|
||||
this.text1 = new TextElem(pos, Math.floor(c.w/8), ctx, "white");
|
||||
this.text2 = new TextElem(pos, Math.floor(c.w/24), ctx, "white");
|
||||
this.text3 = new TextElem(pos, Math.floor(c.w/24), ctx, "white");
|
||||
|
||||
// Dotted Midline
|
||||
pos.assign(c.w_mid-c.midLineSize/2, 0+c.wallSize);
|
||||
@@ -110,5 +113,3 @@ class GameComponentsClient extends GameComponentsExtensionForClient {
|
||||
this.h_grid_d1 = new RectangleClient(pos, c.gridSize, c.h, ctx, "darkgreen");
|
||||
}
|
||||
}
|
||||
|
||||
export {GameComponentsClient}
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
export class InitOptions {
|
||||
sound = "off";
|
||||
multi_balls = false;
|
||||
moving_walls = false;
|
||||
isSomeoneIsInvited = false;
|
||||
isInvitedPerson = false;
|
||||
playerOneUsername = "";
|
||||
playerTwoUsername = "";
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
|
||||
import * as en from "../../shared_js/enums.js"
|
||||
import * as ev from "../../shared_js/class/Event.js"
|
||||
import type * as en from "../../shared_js/enums.js"
|
||||
import type * as ev from "../../shared_js/class/Event.js"
|
||||
|
||||
class InputHistory {
|
||||
export class InputHistory {
|
||||
input: en.InputEnum;
|
||||
id: number;
|
||||
deltaTime: number;
|
||||
@@ -12,5 +12,3 @@ class InputHistory {
|
||||
this.deltaTime = deltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
export {InputHistory}
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
import { Vector, VectorInteger } from "../../shared_js/class/Vector.js";
|
||||
import { Component, GraphicComponent, Moving } from "../../shared_js/class/interface.js";
|
||||
import type { GraphicComponent } from "../../shared_js/class/interface.js";
|
||||
import { Rectangle, MovingRectangle, Racket, Ball } from "../../shared_js/class/Rectangle.js";
|
||||
import { soundPongArr } from "../audio.js"
|
||||
import { random } from "../utils.js";
|
||||
@@ -17,7 +17,7 @@ function clearRectangle(this: RectangleClient, pos?: VectorInteger) {
|
||||
this.ctx.clearRect(this.pos.x, this.pos.y, this.width, this.height);
|
||||
}
|
||||
|
||||
class RectangleClient extends Rectangle implements GraphicComponent {
|
||||
export class RectangleClient extends Rectangle implements GraphicComponent {
|
||||
ctx: CanvasRenderingContext2D;
|
||||
color: string;
|
||||
update: () => void;
|
||||
@@ -31,19 +31,9 @@ class RectangleClient extends Rectangle implements GraphicComponent {
|
||||
this.update = updateRectangle;
|
||||
this.clear = clearRectangle;
|
||||
}
|
||||
// update() {
|
||||
// this.ctx.fillStyle = this.color;
|
||||
// this.ctx.fillRect(this.pos.x, this.pos.y, this.width, this.height);
|
||||
// }
|
||||
// clear(pos?: VectorInteger) {
|
||||
// if (pos)
|
||||
// this.ctx.clearRect(pos.x, pos.y, this.width, this.height);
|
||||
// else
|
||||
// this.ctx.clearRect(this.pos.x, this.pos.y, this.width, this.height);
|
||||
// }
|
||||
}
|
||||
|
||||
class MovingRectangleClient extends MovingRectangle implements GraphicComponent {
|
||||
export class MovingRectangleClient extends MovingRectangle implements GraphicComponent {
|
||||
ctx: CanvasRenderingContext2D;
|
||||
color: string;
|
||||
update: () => void;
|
||||
@@ -59,7 +49,7 @@ class MovingRectangleClient extends MovingRectangle implements GraphicComponent
|
||||
}
|
||||
}
|
||||
|
||||
class RacketClient extends Racket implements GraphicComponent {
|
||||
export class RacketClient extends Racket implements GraphicComponent {
|
||||
ctx: CanvasRenderingContext2D;
|
||||
color: string;
|
||||
update: () => void;
|
||||
@@ -75,7 +65,7 @@ class RacketClient extends Racket implements GraphicComponent {
|
||||
}
|
||||
}
|
||||
|
||||
class BallClient extends Ball implements GraphicComponent {
|
||||
export class BallClient extends Ball implements GraphicComponent {
|
||||
ctx: CanvasRenderingContext2D;
|
||||
color: string;
|
||||
update: () => void;
|
||||
@@ -91,12 +81,10 @@ class BallClient extends Ball implements GraphicComponent {
|
||||
}
|
||||
bounce(collider?: Rectangle) {
|
||||
this._bounceAlgo(collider);
|
||||
soundPongArr[ Math.floor(random(0, soundPongArr.length)) ].play();
|
||||
let i = Math.floor(random(0, soundPongArr.length));
|
||||
soundPongArr[ i ].play();
|
||||
console.log(`sound_i=${i}`); // debug log
|
||||
}
|
||||
/* protected _bounceRacket(collider: Racket) {
|
||||
this._bounceRacketAlgo(collider);
|
||||
soundRoblox.play();
|
||||
} */
|
||||
}
|
||||
|
||||
function updateLine(this: Line) {
|
||||
@@ -105,17 +93,20 @@ function updateLine(this: Line) {
|
||||
let i = 0;
|
||||
while (i < this.segmentCount)
|
||||
{
|
||||
// for Horizontal Line
|
||||
/* Horizontal Line */
|
||||
// pos.y = this.pos.y;
|
||||
// pos.x = this.pos.x + this.segmentWidth * i;
|
||||
|
||||
/* Vertical Line */
|
||||
pos.x = this.pos.x;
|
||||
pos.y = this.pos.y + this.segmentHeight * i;
|
||||
|
||||
this.ctx.fillRect(pos.x, pos.y, this.segmentWidth, this.segmentHeight);
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
|
||||
class Line extends RectangleClient {
|
||||
export class Line extends RectangleClient {
|
||||
gapeCount: number = 0;
|
||||
segmentCount: number;
|
||||
segmentWidth: number;
|
||||
@@ -129,13 +120,12 @@ class Line extends RectangleClient {
|
||||
this.gapeCount = gapeCount;
|
||||
this.segmentCount = this.gapeCount * 2 + 1;
|
||||
|
||||
/* Vertical Line */
|
||||
this.segmentWidth = this.width;
|
||||
this.segmentHeight = this.height / this.segmentCount;
|
||||
|
||||
// for Horizontal Line
|
||||
/* Horizontal Line */
|
||||
// this.segmentWidth = this.width / this.segmentCount;
|
||||
// this.segmentHeight = this.height;
|
||||
}
|
||||
}
|
||||
|
||||
export {RectangleClient, MovingRectangleClient, RacketClient, BallClient, Line}
|
||||
@@ -1,9 +1,9 @@
|
||||
|
||||
import { Vector, VectorInteger } from "../../shared_js/class/Vector.js";
|
||||
import { Component } from "../../shared_js/class/interface.js";
|
||||
import type { Component } from "../../shared_js/class/interface.js";
|
||||
|
||||
// conflict with Text
|
||||
class TextElem implements Component {
|
||||
export class TextElem implements Component {
|
||||
ctx: CanvasRenderingContext2D;
|
||||
pos: VectorInteger;
|
||||
color: string;
|
||||
@@ -39,7 +39,7 @@ class TextElem implements Component {
|
||||
}
|
||||
}
|
||||
|
||||
class TextNumericValue extends TextElem {
|
||||
export class TextNumericValue extends TextElem {
|
||||
private _value: number = 0;
|
||||
constructor(pos: VectorInteger, size: number,
|
||||
ctx: CanvasRenderingContext2D, color: string, font?: string)
|
||||
@@ -54,5 +54,3 @@ class TextNumericValue extends TextElem {
|
||||
this.text = v.toString();
|
||||
}
|
||||
}
|
||||
|
||||
export {TextElem, TextNumericValue}
|
||||
@@ -1,10 +1,8 @@
|
||||
|
||||
import { pong, gc } from "./global.js"
|
||||
import * as c from "./constants.js"
|
||||
import * as en from "../shared_js/enums.js"
|
||||
import { gridDisplay } from "./handleInput.js";
|
||||
|
||||
function drawLoop()
|
||||
export function drawLoop()
|
||||
{
|
||||
pong.clear();
|
||||
|
||||
@@ -15,6 +13,8 @@ function drawLoop()
|
||||
drawStatic();
|
||||
|
||||
gc.text1.update();
|
||||
gc.text2.update();
|
||||
gc.text3.update();
|
||||
|
||||
drawDynamic();
|
||||
}
|
||||
@@ -47,5 +47,3 @@ function drawGrid()
|
||||
gc.h_grid_u1.update();
|
||||
gc.h_grid_d1.update();
|
||||
}
|
||||
|
||||
export {drawLoop}
|
||||
@@ -0,0 +1,71 @@
|
||||
|
||||
import * as c from "./constants.js";
|
||||
import * as en from "../shared_js/enums.js"
|
||||
import { gc, matchOptions, clientInfo, clientInfoSpectator} from "./global.js";
|
||||
import { wallsMovements } from "../shared_js/wallsMovement.js";
|
||||
import type { RacketClient } from "./class/RectangleClient.js";
|
||||
import type { VectorInteger } from "../shared_js/class/Vector.js";
|
||||
|
||||
let actual_time: number = Date.now();
|
||||
let last_time: number;
|
||||
let delta_time: number;
|
||||
|
||||
export function gameLoop()
|
||||
{
|
||||
/* last_time = actual_time;
|
||||
actual_time = Date.now();
|
||||
delta_time = (actual_time - last_time) / 1000; */
|
||||
|
||||
delta_time = c.fixedDeltaTime;
|
||||
// console.log(`delta_gameLoop: ${delta_time}`);
|
||||
|
||||
// interpolation
|
||||
// console.log(`dir.y: ${clientInfo.opponent.dir.y}, pos.y: ${clientInfo.opponent.pos.y}, opponentNextPos.y: ${clientInfo.opponentNextPos.y}`);
|
||||
if (clientInfo.opponent.dir.y != 0 ) {
|
||||
racketInterpolation(delta_time, clientInfo.opponent, clientInfo.opponentNextPos);
|
||||
}
|
||||
|
||||
// client prediction
|
||||
gc.ballsArr.forEach((ball) => {
|
||||
ball.moveAndBounce(delta_time, [gc.wallTop, gc.wallBottom, gc.playerLeft, gc.playerRight]);
|
||||
});
|
||||
|
||||
if (matchOptions & en.MatchOptions.movingWalls) {
|
||||
wallsMovements(delta_time, gc);
|
||||
}
|
||||
}
|
||||
|
||||
export function gameLoopSpectator()
|
||||
{
|
||||
delta_time = c.fixedDeltaTime;
|
||||
|
||||
// interpolation
|
||||
if (gc.playerLeft.dir.y != 0 ) {
|
||||
racketInterpolation(delta_time, gc.playerLeft, clientInfoSpectator.playerLeftNextPos);
|
||||
}
|
||||
if (gc.playerRight.dir.y != 0 ) {
|
||||
racketInterpolation(delta_time, gc.playerRight, clientInfoSpectator.playerRightNextPos);
|
||||
}
|
||||
|
||||
// client prediction
|
||||
gc.ballsArr.forEach((ball) => {
|
||||
ball.moveAndBounce(delta_time, [gc.wallTop, gc.wallBottom, gc.playerLeft, gc.playerRight]);
|
||||
});
|
||||
|
||||
if (matchOptions & en.MatchOptions.movingWalls) {
|
||||
wallsMovements(delta_time, gc);
|
||||
}
|
||||
}
|
||||
|
||||
function racketInterpolation(delta: number, racket: RacketClient, nextPos: VectorInteger)
|
||||
{
|
||||
// interpolation
|
||||
racket.moveAndCollide(delta, [gc.wallTop, gc.wallBottom]);
|
||||
|
||||
if ((racket.dir.y > 0 && racket.pos.y > nextPos.y)
|
||||
|| (racket.dir.y < 0 && racket.pos.y < nextPos.y))
|
||||
{
|
||||
racket.dir.y = 0;
|
||||
racket.pos.y = nextPos.y;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
|
||||
import * as en from "../shared_js/enums.js";
|
||||
import type { GameArea } from "./class/GameArea.js";
|
||||
import type { GameComponentsClient } from "./class/GameComponentsClient.js";
|
||||
|
||||
// export {pong, gc, matchOptions} from "./pong.js"
|
||||
export {socket, clientInfo, clientInfoSpectator} from "./ws.js"
|
||||
|
||||
export let pong: GameArea;
|
||||
export let gc: GameComponentsClient;
|
||||
export let matchOptions: en.MatchOptions = en.MatchOptions.noOption;
|
||||
|
||||
export function initPong(value: GameArea) {
|
||||
pong = value;
|
||||
}
|
||||
|
||||
export function initGc(value: GameComponentsClient) {
|
||||
gc = value;
|
||||
}
|
||||
|
||||
export function initMatchOptions(value: en.MatchOptions) {
|
||||
matchOptions = value;
|
||||
}
|
||||
|
||||
export let startFunction: () => void;
|
||||
|
||||
export function initStartFunction(value: () => void) {
|
||||
startFunction = value;
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import * as ev from "../shared_js/class/Event.js"
|
||||
import * as en from "../shared_js/enums.js"
|
||||
import { InputHistory } from "./class/InputHistory.js"
|
||||
import * as c from "./constants.js";
|
||||
import { matchEnded } from "./ws.js";
|
||||
|
||||
export let gridDisplay = false;
|
||||
|
||||
@@ -20,7 +21,7 @@ const inputHistoryArr: InputHistory[] = [];
|
||||
socket.send(JSON.stringify(inputState));
|
||||
} */
|
||||
|
||||
function handleInput()
|
||||
export function handleInput()
|
||||
{
|
||||
/* last_time = actual_time;
|
||||
actual_time = Date.now();
|
||||
@@ -43,7 +44,9 @@ function handleInput()
|
||||
playerMovements(delta_time, keys);
|
||||
}
|
||||
|
||||
socket.send(JSON.stringify(inputState));
|
||||
if (!matchEnded) {
|
||||
socket.send(JSON.stringify(inputState));
|
||||
}
|
||||
// setTimeout(testInputDelay, 100);
|
||||
inputHistoryArr.push(new InputHistory(inputState, delta_time));
|
||||
|
||||
@@ -86,7 +89,7 @@ function playerMovePrediction(delta: number, input: en.InputEnum)
|
||||
racket.moveAndCollide(delta, [gc.wallTop, gc.wallBottom]);
|
||||
}
|
||||
|
||||
function repeatInput(lastInputId: number)
|
||||
export function repeatInput(lastInputId: number)
|
||||
{
|
||||
// server reconciliation
|
||||
let i = inputHistoryArr.findIndex((value: InputHistory) => {
|
||||
@@ -106,5 +109,3 @@ function repeatInput(lastInputId: number)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export {handleInput, repeatInput}
|
||||
@@ -0,0 +1,47 @@
|
||||
|
||||
import * as c from "./constants.js"
|
||||
import * as en from "../shared_js/enums.js"
|
||||
import { GameArea } from "./class/GameArea.js";
|
||||
import { GameComponentsClient } from "./class/GameComponentsClient.js";
|
||||
import { socket } from "./ws.js";
|
||||
import { initAudio } from "./audio.js";
|
||||
import type { InitOptions } from "./class/InitOptions.js";
|
||||
|
||||
import { pong } from "./global.js"
|
||||
import { initPong, initGc, initMatchOptions } from "./global.js"
|
||||
|
||||
export function computeMatchOptions(options: InitOptions)
|
||||
{
|
||||
let matchOptions = en.MatchOptions.noOption;
|
||||
|
||||
if (options.multi_balls === true) {
|
||||
matchOptions |= en.MatchOptions.multiBalls
|
||||
}
|
||||
if (options.moving_walls === true) {
|
||||
matchOptions |= en.MatchOptions.movingWalls
|
||||
}
|
||||
|
||||
return matchOptions;
|
||||
}
|
||||
|
||||
export function initBase(matchOptions: en.MatchOptions, sound: string, gameAreaId: string)
|
||||
{
|
||||
initMatchOptions(matchOptions);
|
||||
initAudio(sound);
|
||||
initPong(new GameArea(gameAreaId));
|
||||
initGc(new GameComponentsClient(matchOptions, pong.ctx));
|
||||
}
|
||||
|
||||
export function destroyBase()
|
||||
{
|
||||
if (pong)
|
||||
{
|
||||
clearInterval(pong.handleInputInterval);
|
||||
clearInterval(pong.gameLoopInterval);
|
||||
clearInterval(pong.drawLoopInterval);
|
||||
initPong(null);
|
||||
}
|
||||
if (socket && socket.OPEN) {
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
|
||||
import * as c from "./constants.js"
|
||||
import { gc, pong } from "./global.js"
|
||||
import * as en from "../shared_js/enums.js"
|
||||
|
||||
/*
|
||||
before game
|
||||
*/
|
||||
export function error(message: string)
|
||||
{
|
||||
console.log("msg.error()");
|
||||
pong.clear();
|
||||
const text = "error: " + message;
|
||||
console.log(text);
|
||||
gc.text2.clear();
|
||||
gc.text2.pos.assign(c.w*0.2, c.h*0.5);
|
||||
gc.text2.text = text;
|
||||
gc.text2.update();
|
||||
}
|
||||
|
||||
export function matchmaking()
|
||||
{
|
||||
const text = "searching...";
|
||||
console.log(text);
|
||||
gc.text1.clear();
|
||||
gc.text1.pos.assign(c.w*0.2, c.h*0.5);
|
||||
gc.text1.text = text;
|
||||
gc.text1.update();
|
||||
}
|
||||
|
||||
export function matchmakingComplete()
|
||||
{
|
||||
const text = "match found !";
|
||||
console.log(text);
|
||||
gc.text1.clear();
|
||||
gc.text1.pos.assign(c.w*0.15, c.h*0.5);
|
||||
gc.text1.text = text;
|
||||
gc.text1.update();
|
||||
}
|
||||
|
||||
export function matchAbort()
|
||||
{
|
||||
const text = "match abort";
|
||||
console.log(text);
|
||||
gc.text1.clear();
|
||||
gc.text1.pos.assign(c.w*0.15, c.h*0.5);
|
||||
gc.text1.text = text;
|
||||
gc.text1.update();
|
||||
|
||||
setTimeout(() => {
|
||||
gc.text2.pos.assign(c.w*0.44, c.h*0.6);
|
||||
gc.text2.text = "pardon =(";
|
||||
const oriSize = gc.text2.size;
|
||||
gc.text2.size = c.w*0.025;
|
||||
gc.text2.update();
|
||||
gc.text2.size = oriSize;
|
||||
}, 2500);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
in game
|
||||
*/
|
||||
export function win()
|
||||
{
|
||||
gc.text1.pos.assign(c.w*0.415, c.h*0.5);
|
||||
gc.text1.text = "WIN";
|
||||
}
|
||||
|
||||
export function lose()
|
||||
{
|
||||
gc.text1.pos.assign(c.w*0.383, c.h*0.5);
|
||||
gc.text1.text = "LOSE";
|
||||
}
|
||||
|
||||
export function forfeit(playerSide: en.PlayerSide)
|
||||
{
|
||||
if (playerSide === en.PlayerSide.left) {
|
||||
gc.text2.pos.assign(c.w*0.65, c.h*0.42);
|
||||
gc.text3.pos.assign(c.w*0.65, c.h*0.52);
|
||||
}
|
||||
else {
|
||||
gc.text2.pos.assign(c.w*0.09, c.h*0.42);
|
||||
gc.text3.pos.assign(c.w*0.09, c.h*0.52);
|
||||
}
|
||||
setTimeout(() => {
|
||||
gc.text2.text = "par forfait";
|
||||
}, 1500);
|
||||
setTimeout(() => {
|
||||
gc.text3.text = "calme ta joie";
|
||||
}, 3500);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
|
||||
import * as c from "./constants.js"
|
||||
import { handleInput } from "./handleInput.js";
|
||||
import { gameLoop } from "./gameLoop.js"
|
||||
import { drawLoop } from "./draw.js";
|
||||
import { countdown } from "./utils.js";
|
||||
import { initWebSocket } from "./ws.js";
|
||||
import type { InitOptions } from "./class/InitOptions.js";
|
||||
export { InitOptions } from "./class/InitOptions.js";
|
||||
import { initBase, destroyBase, computeMatchOptions } from "./init.js";
|
||||
export { computeMatchOptions } from "./init.js";
|
||||
|
||||
/* TODO: A way to delay the init of variables, but still use "const" not "let" ? */
|
||||
import { pong, gc } from "./global.js"
|
||||
import { initStartFunction } from "./global.js"
|
||||
|
||||
|
||||
export function init(options: InitOptions, gameAreaId: string, token: string)
|
||||
{
|
||||
const matchOptions = computeMatchOptions(options);
|
||||
initBase(matchOptions, options.sound, gameAreaId);
|
||||
|
||||
initStartFunction(start);
|
||||
if (options.isSomeoneIsInvited) {
|
||||
initWebSocket(matchOptions, token, options.playerOneUsername, true, options.playerTwoUsername, options.isInvitedPerson);
|
||||
}
|
||||
else {
|
||||
initWebSocket(matchOptions, token, options.playerOneUsername);
|
||||
}
|
||||
}
|
||||
|
||||
export function destroy()
|
||||
{
|
||||
destroyBase();
|
||||
}
|
||||
|
||||
function start()
|
||||
{
|
||||
gc.text1.pos.assign(c.w*0.5, c.h*0.75);
|
||||
countdown(c.matchStartDelay/1000, (count: number) => {
|
||||
gc.text1.clear();
|
||||
gc.text1.text = `${count}`;
|
||||
gc.text1.update();
|
||||
}, resume);
|
||||
}
|
||||
|
||||
function resume()
|
||||
{
|
||||
gc.text1.text = "";
|
||||
window.addEventListener('keydown', function (e) {
|
||||
pong.addKey(e.key);
|
||||
});
|
||||
window.addEventListener('keyup', function (e) {
|
||||
pong.deleteKey(e.key);
|
||||
});
|
||||
pong.handleInputInterval = window.setInterval(handleInput, c.handleInputIntervalMS);
|
||||
// pong.handleInputInterval = window.setInterval(sendLoop, c.sendLoopIntervalMS);
|
||||
pong.gameLoopInterval = window.setInterval(gameLoop, c.gameLoopIntervalMS);
|
||||
pong.drawLoopInterval = window.setInterval(drawLoop, c.drawLoopIntervalMS);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
|
||||
import * as c from "./constants.js"
|
||||
import type * as en from "../shared_js/enums.js"
|
||||
import { gameLoopSpectator } from "./gameLoop.js"
|
||||
import { drawLoop } from "./draw.js";
|
||||
import { initWebSocketSpectator } from "./ws.js";
|
||||
import { initBase, destroyBase, computeMatchOptions } from "./init.js";
|
||||
export { computeMatchOptions } from "./init.js";
|
||||
|
||||
/* TODO: A way to delay the init of variables, but still use "const" not "let" ? */
|
||||
import { pong, gc } from "./global.js"
|
||||
import { initStartFunction } from "./global.js"
|
||||
|
||||
|
||||
export function init(matchOptions: en.MatchOptions, sound: string, gameAreaId: string, gameSessionId: string)
|
||||
{
|
||||
initBase(matchOptions, sound, gameAreaId);
|
||||
|
||||
initStartFunction(start);
|
||||
initWebSocketSpectator(gameSessionId);
|
||||
}
|
||||
|
||||
export function destroy()
|
||||
{
|
||||
destroyBase();
|
||||
}
|
||||
|
||||
function start()
|
||||
{
|
||||
resume();
|
||||
}
|
||||
|
||||
function resume()
|
||||
{
|
||||
pong.gameLoopInterval = window.setInterval(gameLoopSpectator, c.gameLoopIntervalMS);
|
||||
pong.drawLoopInterval = window.setInterval(drawLoop, c.drawLoopIntervalMS);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
export * from "../shared_js/utils.js"
|
||||
|
||||
function countdown(count: number, callback?: (count: number) => void, endCallback?: () => void)
|
||||
export function countdown(count: number, callback?: (count: number) => void, endCallback?: () => void)
|
||||
{
|
||||
console.log("countdown ", count);
|
||||
if (count > 0) {
|
||||
@@ -14,5 +14,3 @@ function countdown(count: number, callback?: (count: number) => void, endCallbac
|
||||
endCallback();
|
||||
}
|
||||
}
|
||||
|
||||
export {countdown}
|
||||
@@ -1,15 +1,18 @@
|
||||
|
||||
import * as c from "./constants.js"
|
||||
import { gc, matchOptions } from "./global.js"
|
||||
import { gc, matchOptions, startFunction } from "./global.js"
|
||||
import * as ev from "../shared_js/class/Event.js"
|
||||
import * as en from "../shared_js/enums.js"
|
||||
import { matchmaking, matchmakingComplete, startGame } from "./pong.js";
|
||||
import { RacketClient } from "./class/RectangleClient.js";
|
||||
import * as msg from "./message.js";
|
||||
import type { RacketClient } from "./class/RectangleClient.js";
|
||||
import { repeatInput } from "./handleInput.js";
|
||||
import { soundRoblox } from "./audio.js"
|
||||
import { sleep } from "./utils.js";
|
||||
import { Vector, VectorInteger } from "../shared_js/class/Vector.js";
|
||||
|
||||
export let matchEnded = false;
|
||||
export let matchAbort = false;
|
||||
|
||||
class ClientInfo {
|
||||
id = "";
|
||||
side: en.PlayerSide;
|
||||
@@ -18,18 +21,34 @@ class ClientInfo {
|
||||
opponentNextPos: VectorInteger;
|
||||
}
|
||||
|
||||
const wsPort = 8042;
|
||||
const wsUrl = "ws://" + document.location.hostname + ":" + wsPort + "/pong";
|
||||
class ClientInfoSpectator {
|
||||
// side: en.PlayerSide;
|
||||
/* WIP: playerLeftNextPos and playerRightNextPos could be in clientInfo for simplicity */
|
||||
playerLeftNextPos: VectorInteger;
|
||||
playerRightNextPos: VectorInteger;
|
||||
}
|
||||
|
||||
const wsUrl = "ws://transcendance:8080/pong";
|
||||
export let socket: WebSocket; /* TODO: A way to still use "const" not "let" ? */
|
||||
export const clientInfo = new ClientInfo();
|
||||
export const clientInfoSpectator = new ClientInfoSpectator(); // WIP, could refactor this
|
||||
|
||||
export function initWebSocket(options: en.MatchOptions)
|
||||
|
||||
export function initWebSocket(options: en.MatchOptions, token: string, username: string, privateMatch = false, playerTwoUsername?: string, isInvitedPerson? : boolean)
|
||||
{
|
||||
socket = new WebSocket(wsUrl, "json");
|
||||
console.log("Infos from ws.ts : options => " + options + " token => " + token + " username => " + username + " priavte match => " + privateMatch
|
||||
+ " player two => " + playerTwoUsername)
|
||||
socket.addEventListener("open", (event) => {
|
||||
socket.send(JSON.stringify( new ev.ClientAnnounce(en.ClientRole.player, options, clientInfo.id) ));
|
||||
if (privateMatch) {
|
||||
socket.send(JSON.stringify( new ev.ClientAnnouncePlayer(options, token, username, privateMatch, playerTwoUsername, isInvitedPerson) ));
|
||||
}
|
||||
else {
|
||||
socket.send(JSON.stringify( new ev.ClientAnnouncePlayer(options, token, username) ));
|
||||
}
|
||||
});
|
||||
// socket.addEventListener("message", logListener); // for testing purpose
|
||||
socket.addEventListener("message", errorListener);
|
||||
socket.addEventListener("message", preMatchListener);
|
||||
}
|
||||
|
||||
@@ -37,6 +56,15 @@ function logListener(this: WebSocket, event: MessageEvent) {
|
||||
console.log("%i: " + event.data, Date.now());
|
||||
}
|
||||
|
||||
function errorListener(this: WebSocket, event: MessageEvent) {
|
||||
console.log("errorListener");
|
||||
const data: ev.ServerEvent = JSON.parse(event.data);
|
||||
if (data.type === en.EventTypes.error) {
|
||||
console.log("actual Error");
|
||||
msg.error((data as ev.EventError).message);
|
||||
}
|
||||
}
|
||||
|
||||
function preMatchListener(this: WebSocket, event: MessageEvent)
|
||||
{
|
||||
const data: ev.ServerEvent = JSON.parse(event.data);
|
||||
@@ -45,7 +73,7 @@ function preMatchListener(this: WebSocket, event: MessageEvent)
|
||||
clientInfo.id = (<ev.EventAssignId>data).id;
|
||||
break;
|
||||
case en.EventTypes.matchmakingInProgress:
|
||||
matchmaking();
|
||||
msg.matchmaking();
|
||||
break;
|
||||
case en.EventTypes.matchmakingComplete:
|
||||
clientInfo.side = (<ev.EventMatchmakingComplete>data).side;
|
||||
@@ -62,17 +90,25 @@ function preMatchListener(this: WebSocket, event: MessageEvent)
|
||||
clientInfo.opponentNextPos = new VectorInteger(clientInfo.opponent.pos.x, clientInfo.opponent.pos.y);
|
||||
clientInfo.racket.color = "darkgreen"; // for testing purpose
|
||||
socket.send(JSON.stringify( new ev.ClientEvent(en.EventTypes.clientPlayerReady) )); // TODO: set an interval/timeout to resend until matchStart response (in case of network problem)
|
||||
matchmakingComplete();
|
||||
msg.matchmakingComplete();
|
||||
break;
|
||||
case en.EventTypes.matchStart:
|
||||
socket.removeEventListener("message", preMatchListener);
|
||||
socket.addEventListener("message", inGameListener);
|
||||
startGame();
|
||||
startFunction();
|
||||
break;
|
||||
case en.EventTypes.matchAbort:
|
||||
matchAbort = true;
|
||||
socket.removeEventListener("message", preMatchListener);
|
||||
msg.matchAbort();
|
||||
setTimeout(() => {
|
||||
matchAbort = false;
|
||||
}, 1000);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function inGameListener(event: MessageEvent)
|
||||
function inGameListener(this: WebSocket, event: MessageEvent)
|
||||
{
|
||||
const data: ev.ServerEvent = JSON.parse(event.data);
|
||||
switch (data.type) {
|
||||
@@ -168,15 +204,126 @@ function scoreUpdate(data: ev.EventScoreUpdate)
|
||||
|
||||
function matchEnd(data: ev.EventMatchEnd)
|
||||
{
|
||||
matchEnded = true;
|
||||
socket.close();
|
||||
if (data.winner === clientInfo.side) {
|
||||
gc.text1.pos.assign(c.w*0.415, c.h_mid);
|
||||
gc.text1.text = "WIN";
|
||||
msg.win();
|
||||
if (data.forfeit) {
|
||||
msg.forfeit(clientInfo.side);
|
||||
}
|
||||
}
|
||||
else {
|
||||
gc.text1.pos.assign(c.w*0.383, c.h_mid);
|
||||
gc.text1.text = "LOSE";
|
||||
msg.lose();
|
||||
}
|
||||
// matchEnded = true;
|
||||
setTimeout(() => {
|
||||
matchEnded = false;
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
/* Spectator */
|
||||
|
||||
export function initWebSocketSpectator(gameSessionId: string)
|
||||
{
|
||||
socket = new WebSocket(wsUrl, "json");
|
||||
socket.addEventListener("open", (event) => {
|
||||
socket.send(JSON.stringify( new ev.ClientAnnounceSpectator(gameSessionId) ));
|
||||
});
|
||||
// socket.addEventListener("message", logListener); // for testing purpose
|
||||
socket.addEventListener("message", preMatchListenerSpectator);
|
||||
|
||||
clientInfoSpectator.playerLeftNextPos = new VectorInteger(gc.playerLeft.pos.x, gc.playerLeft.pos.y);
|
||||
clientInfoSpectator.playerRightNextPos = new VectorInteger(gc.playerRight.pos.x, gc.playerRight.pos.y);
|
||||
|
||||
}
|
||||
|
||||
export function preMatchListenerSpectator(this: WebSocket, event: MessageEvent)
|
||||
{
|
||||
const data: ev.ServerEvent = JSON.parse(event.data);
|
||||
if (data.type === en.EventTypes.matchStart)
|
||||
{
|
||||
socket.removeEventListener("message", preMatchListenerSpectator);
|
||||
socket.addEventListener("message", inGameListenerSpectator);
|
||||
startFunction();
|
||||
}
|
||||
}
|
||||
|
||||
function inGameListenerSpectator(this: WebSocket, event: MessageEvent)
|
||||
{
|
||||
const data: ev.ServerEvent = JSON.parse(event.data);
|
||||
switch (data.type) {
|
||||
case en.EventTypes.gameUpdate:
|
||||
gameUpdateSpectator(data as ev.EventGameUpdate);
|
||||
break;
|
||||
case en.EventTypes.scoreUpdate:
|
||||
scoreUpdateSpectator(data as ev.EventScoreUpdate);
|
||||
break;
|
||||
case en.EventTypes.matchEnd:
|
||||
matchEndSpectator(data as ev.EventMatchEnd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function gameUpdateSpectator(data: ev.EventGameUpdate)
|
||||
{
|
||||
console.log("gameUpdateSpectator");
|
||||
|
||||
if (matchOptions & en.MatchOptions.movingWalls) {
|
||||
gc.wallTop.pos.y = data.wallTop.y;
|
||||
gc.wallBottom.pos.y = data.wallBottom.y;
|
||||
}
|
||||
|
||||
data.ballsArr.forEach((ball, i) => {
|
||||
gc.ballsArr[i].pos.assign(ball.x, ball.y);
|
||||
gc.ballsArr[i].dir.assign(ball.dirX, ball.dirY);
|
||||
gc.ballsArr[i].speed = ball.speed;
|
||||
});
|
||||
|
||||
// interpolation
|
||||
for (const racket of [gc.playerLeft, gc.playerRight])
|
||||
{
|
||||
let nextPos: VectorInteger;
|
||||
if (racket === gc.playerLeft) {
|
||||
nextPos = clientInfoSpectator.playerLeftNextPos;
|
||||
}
|
||||
else {
|
||||
nextPos = clientInfoSpectator.playerRightNextPos;
|
||||
}
|
||||
|
||||
racket.pos.assign(nextPos.x, nextPos.y);
|
||||
if (racket === gc.playerLeft) {
|
||||
nextPos.assign(racket.pos.x, data.playerLeft.y);
|
||||
}
|
||||
else {
|
||||
nextPos.assign(racket.pos.x, data.playerRight.y);
|
||||
}
|
||||
|
||||
racket.dir = new Vector(
|
||||
nextPos.x - racket.pos.x,
|
||||
nextPos.y - racket.pos.y
|
||||
);
|
||||
|
||||
if (Math.abs(racket.dir.x) + Math.abs(racket.dir.y) !== 0) {
|
||||
racket.dir = racket.dir.normalized();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function scoreUpdateSpectator(data: ev.EventScoreUpdate)
|
||||
{
|
||||
console.log("scoreUpdateSpectator");
|
||||
gc.scoreLeft.value = data.scoreLeft;
|
||||
gc.scoreRight.value = data.scoreRight;
|
||||
}
|
||||
|
||||
function matchEndSpectator(data: ev.EventMatchEnd)
|
||||
{
|
||||
console.log("matchEndSpectator");
|
||||
matchEnded = true;
|
||||
socket.close();
|
||||
// WIP
|
||||
/* msg.win();
|
||||
if (data.forfeit) {
|
||||
msg.forfeit(clientInfo.side);
|
||||
} */
|
||||
}
|
||||
|
||||
// export let matchEnded = false;
|
||||
@@ -0,0 +1,144 @@
|
||||
|
||||
import * as en from "../enums.js"
|
||||
|
||||
/* From Server */
|
||||
export class ServerEvent {
|
||||
type: en.EventTypes;
|
||||
constructor(type: en.EventTypes = 0) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
export class EventAssignId extends ServerEvent {
|
||||
id: string;
|
||||
constructor(id: string) {
|
||||
super(en.EventTypes.assignId);
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
export class EventMatchmakingComplete extends ServerEvent {
|
||||
side: en.PlayerSide;
|
||||
constructor(side: en.PlayerSide) {
|
||||
super(en.EventTypes.matchmakingComplete);
|
||||
this.side = side;
|
||||
}
|
||||
}
|
||||
|
||||
export class EventGameUpdate extends ServerEvent {
|
||||
playerLeft = {
|
||||
y: 0
|
||||
};
|
||||
playerRight = {
|
||||
y: 0
|
||||
};
|
||||
ballsArr: {
|
||||
x: number,
|
||||
y: number,
|
||||
dirX: number,
|
||||
dirY: number,
|
||||
speed: number
|
||||
}[] = [];
|
||||
wallTop? = {
|
||||
y: 0
|
||||
};
|
||||
wallBottom? = {
|
||||
y: 0
|
||||
};
|
||||
lastInputId = 0;
|
||||
constructor() { // TODO: constructor that take GameComponentsServer maybe ?
|
||||
super(en.EventTypes.gameUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
export class EventScoreUpdate extends ServerEvent {
|
||||
scoreLeft: number;
|
||||
scoreRight: number;
|
||||
constructor(scoreLeft: number, scoreRight: number) {
|
||||
super(en.EventTypes.scoreUpdate);
|
||||
this.scoreLeft = scoreLeft;
|
||||
this.scoreRight = scoreRight;
|
||||
}
|
||||
}
|
||||
|
||||
export class EventMatchEnd extends ServerEvent {
|
||||
winner: en.PlayerSide;
|
||||
forfeit: boolean;
|
||||
constructor(winner: en.PlayerSide, forfeit = false) {
|
||||
super(en.EventTypes.matchEnd);
|
||||
this.winner = winner;
|
||||
this.forfeit = forfeit;
|
||||
}
|
||||
}
|
||||
|
||||
export class EventMatchAbort extends ServerEvent {
|
||||
constructor() {
|
||||
super(en.EventTypes.matchAbort);
|
||||
}
|
||||
}
|
||||
|
||||
export class EventError extends ServerEvent {
|
||||
message: string;
|
||||
constructor(message: string) {
|
||||
super(en.EventTypes.error);
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* From Client */
|
||||
export class ClientEvent {
|
||||
type: en.EventTypes; // readonly ?
|
||||
constructor(type: en.EventTypes = 0) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
export class ClientAnnounce extends ClientEvent {
|
||||
role: en.ClientRole;
|
||||
constructor(role: en.ClientRole) {
|
||||
super(en.EventTypes.clientAnnounce);
|
||||
this.role = role;
|
||||
}
|
||||
}
|
||||
|
||||
export class ClientAnnouncePlayer extends ClientAnnounce {
|
||||
clientId: string; // unused
|
||||
matchOptions: en.MatchOptions;
|
||||
token: string;
|
||||
username: string;
|
||||
privateMatch: boolean;
|
||||
playerTwoUsername?: string;
|
||||
isInvitedPerson? : boolean;
|
||||
constructor(matchOptions: en.MatchOptions, token: string, username: string, privateMatch: boolean = false, playerTwoUsername?: string, isInvitedPerson? : boolean) {
|
||||
super(en.ClientRole.player);
|
||||
this.matchOptions = matchOptions;
|
||||
this.token = token;
|
||||
this.username = username;
|
||||
this.privateMatch = privateMatch;
|
||||
if (isInvitedPerson) {
|
||||
this.isInvitedPerson = isInvitedPerson;
|
||||
}
|
||||
if (playerTwoUsername) {
|
||||
this.playerTwoUsername = playerTwoUsername;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ClientAnnounceSpectator extends ClientAnnounce {
|
||||
gameSessionId: string;
|
||||
constructor(gameSessionId: string) {
|
||||
super(en.ClientRole.spectator);
|
||||
this.gameSessionId = gameSessionId;
|
||||
}
|
||||
}
|
||||
|
||||
export class EventInput extends ClientEvent {
|
||||
input: en.InputEnum;
|
||||
id: number;
|
||||
constructor(input: en.InputEnum = en.InputEnum.noInput, id: number = 0) {
|
||||
super(en.EventTypes.clientInput);
|
||||
this.input = input;
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import { VectorInteger } from "./Vector.js";
|
||||
import { Rectangle, MovingRectangle, Racket, Ball } from "./Rectangle.js";
|
||||
import { random } from "../utils.js";
|
||||
|
||||
class GameComponents {
|
||||
export class GameComponents {
|
||||
wallTop: Rectangle | MovingRectangle;
|
||||
wallBottom: Rectangle | MovingRectangle;
|
||||
playerLeft: Racket;
|
||||
@@ -61,5 +61,3 @@ class GameComponents {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {GameComponents}
|
||||
@@ -1,9 +1,9 @@
|
||||
|
||||
import { Vector, VectorInteger } from "./Vector.js";
|
||||
import { Component, Moving } from "./interface.js";
|
||||
import type { Component, Moving } from "./interface.js";
|
||||
import * as c from "../constants.js"
|
||||
|
||||
class Rectangle implements Component {
|
||||
export class Rectangle implements Component {
|
||||
pos: VectorInteger;
|
||||
width: number;
|
||||
height: number;
|
||||
@@ -33,7 +33,7 @@ class Rectangle implements Component {
|
||||
}
|
||||
}
|
||||
|
||||
class MovingRectangle extends Rectangle implements Moving {
|
||||
export class MovingRectangle extends Rectangle implements Moving {
|
||||
dir: Vector = new Vector(0,0);
|
||||
speed: number;
|
||||
readonly baseSpeed: number;
|
||||
@@ -61,7 +61,7 @@ class MovingRectangle extends Rectangle implements Moving {
|
||||
}
|
||||
}
|
||||
|
||||
class Racket extends MovingRectangle {
|
||||
export class Racket extends MovingRectangle {
|
||||
constructor(pos: VectorInteger, width: number, height: number, baseSpeed: number) {
|
||||
super(pos, width, height, baseSpeed);
|
||||
}
|
||||
@@ -72,13 +72,22 @@ class Racket extends MovingRectangle {
|
||||
}
|
||||
}
|
||||
|
||||
class Ball extends MovingRectangle {
|
||||
export class Ball extends MovingRectangle {
|
||||
readonly speedIncrease: number;
|
||||
ballInPlay: boolean = false;
|
||||
constructor(pos: VectorInteger, size: number, baseSpeed: number, speedIncrease: number) {
|
||||
super(pos, size, size, baseSpeed);
|
||||
this.speedIncrease = speedIncrease;
|
||||
}
|
||||
moveAndBounce(delta: number, colliderArr: Rectangle[]) {
|
||||
this.move(delta);
|
||||
let i = colliderArr.findIndex(this.collision, this);
|
||||
if (i != -1)
|
||||
{
|
||||
this.bounce(colliderArr[i]);
|
||||
this.move(delta);
|
||||
}
|
||||
}
|
||||
bounce(collider?: Rectangle) {
|
||||
this._bounceAlgo(collider);
|
||||
}
|
||||
@@ -92,15 +101,6 @@ class Ball extends MovingRectangle {
|
||||
this._bounceWall();
|
||||
}
|
||||
}
|
||||
moveAndBounce(delta: number, colliderArr: Rectangle[]) {
|
||||
this.move(delta);
|
||||
let i = colliderArr.findIndex(this.collision, this);
|
||||
if (i != -1)
|
||||
{
|
||||
this.bounce(colliderArr[i]);
|
||||
this.move(delta);
|
||||
}
|
||||
}
|
||||
protected _bounceWall() { // Should be enough for Wall
|
||||
this.dir.y = this.dir.y * -1;
|
||||
}
|
||||
@@ -140,5 +140,3 @@ class Ball extends MovingRectangle {
|
||||
// console.log(`x: ${this.dir.x}, y: ${this.dir.y}`);
|
||||
}
|
||||
}
|
||||
|
||||
export {Rectangle, MovingRectangle, Racket, Ball}
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
class Vector {
|
||||
export class Vector {
|
||||
x: number;
|
||||
y: number;
|
||||
constructor(x: number = 0, y: number = 0) {
|
||||
@@ -16,13 +16,13 @@ class Vector {
|
||||
}
|
||||
}
|
||||
|
||||
class VectorInteger extends Vector {
|
||||
export class VectorInteger extends Vector {
|
||||
// PLACEHOLDER
|
||||
// VectorInteger with set/get dont work (No draw on the screen). Why ?
|
||||
}
|
||||
|
||||
/*
|
||||
class VectorInteger {
|
||||
export class VectorInteger {
|
||||
// private _x: number = 0;
|
||||
// private _y: number = 0;
|
||||
// constructor(x: number = 0, y: number = 0) {
|
||||
@@ -45,5 +45,3 @@ class VectorInteger {
|
||||
// }
|
||||
}
|
||||
*/
|
||||
|
||||
export {Vector, VectorInteger}
|
||||
@@ -1,21 +1,19 @@
|
||||
|
||||
import { Vector, VectorInteger } from "./Vector.js";
|
||||
import type { Vector, VectorInteger } from "./Vector.js";
|
||||
|
||||
interface Component {
|
||||
export interface Component {
|
||||
pos: VectorInteger;
|
||||
}
|
||||
|
||||
interface GraphicComponent extends Component {
|
||||
export interface GraphicComponent extends Component {
|
||||
ctx: CanvasRenderingContext2D;
|
||||
color: string;
|
||||
update: () => void;
|
||||
clear: (pos?: VectorInteger) => void;
|
||||
}
|
||||
|
||||
interface Moving {
|
||||
export interface Moving {
|
||||
dir: Vector;
|
||||
speed: number; // pixel per second
|
||||
move(delta: number): void;
|
||||
}
|
||||
|
||||
export {Component, GraphicComponent, Moving}
|
||||
@@ -11,8 +11,8 @@ export const pw = Math.floor(w*0.017);
|
||||
export const ph = pw*6;
|
||||
export const ballSize = pw;
|
||||
export const wallSize = Math.floor(w*0.01);
|
||||
export const racketSpeed = Math.floor(w*0.66); // pixel per second
|
||||
export const ballSpeed = Math.floor(w*0.66); // pixel per second
|
||||
export const racketSpeed = Math.floor(w*0.60); // pixel per second
|
||||
export const ballSpeed = Math.floor(w*0.55); // pixel per second
|
||||
export const ballSpeedIncrease = Math.floor(ballSpeed*0.05); // pixel per second
|
||||
|
||||
export const normalizedSpeed = false; // for consistency in speed independent of direction
|
||||
@@ -24,3 +24,7 @@ export const newRoundDelay = 1500; // millisecond
|
||||
export const multiBallsCount = 3;
|
||||
export const movingWallPosMax = Math.floor(w*0.12);
|
||||
export const movingWallSpeed = Math.floor(w*0.08);
|
||||
|
||||
|
||||
export const gameSessionIdPLACEHOLDER = "42"; // TESTING SPECTATOR PLACEHOLDER
|
||||
// for testing, force gameSession.id in wsServer.ts->matchmaking()
|
||||
@@ -1,15 +1,17 @@
|
||||
|
||||
enum EventTypes {
|
||||
export enum EventTypes {
|
||||
// Class Implemented
|
||||
gameUpdate = 1,
|
||||
scoreUpdate,
|
||||
matchEnd,
|
||||
assignId,
|
||||
matchmakingComplete,
|
||||
error,
|
||||
|
||||
// Generic
|
||||
matchmakingInProgress,
|
||||
matchStart,
|
||||
matchAbort,
|
||||
matchNewRound, // unused
|
||||
matchPause, // unused
|
||||
matchResume, // unused
|
||||
@@ -21,27 +23,25 @@ enum EventTypes {
|
||||
|
||||
}
|
||||
|
||||
enum InputEnum {
|
||||
export enum InputEnum {
|
||||
noInput = 0,
|
||||
up = 1,
|
||||
down,
|
||||
}
|
||||
|
||||
enum PlayerSide {
|
||||
export enum PlayerSide {
|
||||
left = 1,
|
||||
right
|
||||
}
|
||||
|
||||
enum ClientRole {
|
||||
export enum ClientRole {
|
||||
player = 1,
|
||||
spectator
|
||||
}
|
||||
|
||||
enum MatchOptions {
|
||||
export enum MatchOptions {
|
||||
// binary flags, can be mixed
|
||||
noOption = 0b0,
|
||||
multiBalls = 1 << 0,
|
||||
movingWalls = 1 << 1
|
||||
}
|
||||
|
||||
export {EventTypes, InputEnum, PlayerSide, ClientRole, MatchOptions}
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
import type { MovingRectangle } from "./class/Rectangle.js";
|
||||
|
||||
export function random(min: number = 0, max: number = 1) {
|
||||
return Math.random() * (max - min) + min;
|
||||
}
|
||||
|
||||
export function sleep (ms: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
export function clamp(n: number, min: number, max: number) : number
|
||||
{
|
||||
if (n < min)
|
||||
n = min;
|
||||
else if (n > max)
|
||||
n = max;
|
||||
return (n);
|
||||
}
|
||||
|
||||
// Typescript hack, unused
|
||||
export function assertMovingRectangle(value: unknown): asserts value is MovingRectangle {
|
||||
// if (value !== MovingRectangle) throw new Error("Not a MovingRectangle");
|
||||
return;
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
|
||||
import * as c from "./constants.js";
|
||||
import { MovingRectangle } from "../shared_js/class/Rectangle.js";
|
||||
import { GameComponents } from "./class/GameComponents.js";
|
||||
import type { MovingRectangle } from "../shared_js/class/Rectangle.js";
|
||||
import type { GameComponents } from "./class/GameComponents.js";
|
||||
|
||||
function wallsMovements(delta: number, gc: GameComponents)
|
||||
export function wallsMovements(delta: number, gc: GameComponents)
|
||||
{
|
||||
const wallTop = <MovingRectangle>gc.wallTop;
|
||||
const wallBottom = <MovingRectangle>gc.wallBottom;
|
||||
@@ -16,5 +16,3 @@ function wallsMovements(delta: number, gc: GameComponents)
|
||||
wallTop.moveAndCollide(delta, [gc.playerLeft, gc.playerRight]);
|
||||
wallBottom.moveAndCollide(delta, [gc.playerLeft, gc.playerRight]);
|
||||
}
|
||||
|
||||
export {wallsMovements}
|
||||
@@ -13,7 +13,7 @@
|
||||
.then( (x) => x.json() );
|
||||
})
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<Chat color="bisque"/>
|
||||
@@ -117,7 +117,7 @@
|
||||
/* Glittery Star Stuff */
|
||||
|
||||
|
||||
:root {
|
||||
:root {
|
||||
--purple: rgb(123, 31, 162);
|
||||
--violet: rgb(103, 58, 183);
|
||||
--pink: rgb(244, 143, 177);
|
||||
@@ -128,7 +128,7 @@
|
||||
from {
|
||||
background-position: 0% center;
|
||||
}
|
||||
|
||||
|
||||
to {
|
||||
background-position: -200% center;
|
||||
}
|
||||
@@ -138,7 +138,7 @@
|
||||
from, to {
|
||||
transform: scale(0);
|
||||
}
|
||||
|
||||
|
||||
50% {
|
||||
transform: scale(1);
|
||||
}
|
||||
@@ -148,7 +148,7 @@
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
|
||||
to {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
@@ -193,7 +193,7 @@
|
||||
var(--purple)
|
||||
);
|
||||
background-size: 200%;
|
||||
|
||||
|
||||
/* Keep these for Safari and chrome */
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
<img src="/img/potato_logo.png" alt="Potato Pong Logo" on:click={() => (push('/'))}>
|
||||
<h1>Potato Pong</h1>
|
||||
<nav>
|
||||
<button on:click={() => (push('/game'))}>Game</button>
|
||||
<button on:click={() => (push('/matchlist'))}>Match List</button>
|
||||
<button on:click={() => (push('/ranking'))}>Ranking</button>
|
||||
{#if $location !== '/profile'}
|
||||
<button on:click={() => (push('/profile'))}>My Profile</button>
|
||||
{:else if $location === '/profile'}
|
||||
@@ -52,11 +55,11 @@
|
||||
src:url('/fonts/Bondi.ttf.woff') format('woff'),
|
||||
url('/fonts/Bondi.ttf.svg#Bondi') format('svg'),
|
||||
url('/fonts/Bondi.ttf.eot'),
|
||||
url('/fonts/Bondi.ttf.eot?#iefix') format('embedded-opentype');
|
||||
url('/fonts/Bondi.ttf.eot?#iefix') format('embedded-opentype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* There is a bunch of unncessary shit in here... why so many flex grids, why is everything the same class? just seemed easier but... */
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<script lang="ts">
|
||||
export let match: {
|
||||
id: string,
|
||||
playerOneUsername: string,
|
||||
playerTwoUsername: string
|
||||
};
|
||||
|
||||
</script>
|
||||
<!-- -->
|
||||
|
||||
<li>
|
||||
{match.id} "{match.playerOneUsername}" VS "{match.playerTwoUsername}"
|
||||
</li>
|
||||
|
||||
|
||||
<!-- -->
|
||||
<style>
|
||||
</style>
|
||||
@@ -4,147 +4,19 @@ import SplashPage from "../pages/SplashPage.svelte";
|
||||
import TwoFactorAuthentication from '../pages/TwoFactorAuthentication.svelte';
|
||||
import UnauthorizedAccessPage from '../pages/UnauthorizedAccessPage.svelte';
|
||||
import { wrap } from 'svelte-spa-router/wrap'
|
||||
import { get } from 'svelte/store';
|
||||
import TestPage from '../pages/TmpTestPage.svelte';
|
||||
import Game from '../pages/game/Game.svelte';
|
||||
import Ranking from '../pages/game/Ranking.svelte';
|
||||
import SpectatorMatchList from '../pages/SpectatorMatchList.svelte';
|
||||
|
||||
|
||||
|
||||
// "/article/:title": Article, // this is how you would do parameters!
|
||||
// "/": LoginPage,
|
||||
|
||||
// TMP not using this cuz need to work out how to authentical both 42 and 2FA from the backend
|
||||
|
||||
// export const primaryRoutes = {
|
||||
// '/': SplashPage,
|
||||
// // '/2fa': TwoFactorAuthentication,
|
||||
// '/2fa': wrap({
|
||||
// component: TwoFactorAuthentication,
|
||||
// conditions: [
|
||||
// (detail) => {
|
||||
// // let loggedIn;
|
||||
// // loginStatus.subscribe(value => {
|
||||
// // loggedIn = value;
|
||||
// // });
|
||||
|
||||
// const { fortyTwo, tfa } = get(loginStatus);
|
||||
|
||||
// console.log('condition in /2fa');
|
||||
// // return (loginStatus.fortyTwo && loginStatus.tfa);
|
||||
// // console.log($loginStatus.fortyTwo)
|
||||
// console.log(fortyTwo);
|
||||
// console.log(tfa);
|
||||
// return true;
|
||||
// }
|
||||
// ]
|
||||
// }),
|
||||
// '/profile': wrap({
|
||||
// component: ProfilePage,
|
||||
// conditions: [
|
||||
// (detail) => {
|
||||
// const { fortyTwo, tfa } = get(loginStatus);
|
||||
// // console.log(fortyTwo);
|
||||
// // console.log(tfa);
|
||||
// // return true;
|
||||
// return (fortyTwo && tfa);
|
||||
// }
|
||||
// ]
|
||||
// }),
|
||||
// '/profile/*': wrap({
|
||||
// component: ProfilePage,
|
||||
// conditions: [
|
||||
// (detail) => {
|
||||
// const { fortyTwo, tfa } = get(loginStatus);
|
||||
// // console.log(fortyTwo);
|
||||
// // console.log(tfa);
|
||||
// // return true;
|
||||
// return (fortyTwo && tfa);
|
||||
// }
|
||||
// ]
|
||||
// }),
|
||||
// '/profile': wrap({
|
||||
// // Use a dynamically-loaded component for this
|
||||
// asyncComponent: () => import('./ProfilePage.svelte'),
|
||||
// // Adding one pre-condition that's an async function
|
||||
// conditions: [
|
||||
// async (detail) => {
|
||||
// // Make a network request, which are async operations
|
||||
// const response = await fetch('http://transcendance:8080/api/v2/user')
|
||||
// const data = await response.json()
|
||||
// // Return true to continue loading the component, or false otherwise
|
||||
// if (data.isAdmin) {
|
||||
// return true
|
||||
// }
|
||||
// else {
|
||||
// return false
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// }),
|
||||
// '/unauthorized-access': UnauthorizedAccessPage,
|
||||
// '*': NotFound
|
||||
// };
|
||||
|
||||
export const primaryRoutes = {
|
||||
'/': SplashPage,
|
||||
'/2fa': TwoFactorAuthentication,
|
||||
// '/test': wrap({
|
||||
// component: TestPage,
|
||||
// conditions: [
|
||||
// (detail) => {
|
||||
// const user = get(userStore); // seems like get(store) is not an option
|
||||
// // // const user = userStore;
|
||||
// // // console.log(fortyTwo);
|
||||
// // // console.log(tfa);
|
||||
// // console.log('in /test what is in user')
|
||||
// // console.log(user)
|
||||
|
||||
// // you moron $userStore is a Svelte Abreviation, this is .JS, duh
|
||||
// // let user = $userStore;
|
||||
// // let user;
|
||||
// // const unsub = userStore.subscribe(value => {
|
||||
// // console.log(value);
|
||||
// // user = value;
|
||||
// // });
|
||||
// console.log('in /test what is in userStore directly')
|
||||
// console.log(user)
|
||||
|
||||
// // return true;
|
||||
// // obvi this doesn't work cuz skips to true after no user...
|
||||
// // you gotta make the condition the true and the everything else false
|
||||
// // if (user && user.statusCode && user.statusCode === 403)
|
||||
// // if (user && user.username) {
|
||||
// if (user !== null) {
|
||||
// unsub();
|
||||
// return true;
|
||||
// } else {
|
||||
// unsub();
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// }),
|
||||
'/test': wrap({
|
||||
component: TestPage,
|
||||
conditions: [
|
||||
async(detail) => {
|
||||
// THIS SHIT TOTALLY WORKS
|
||||
// Yea in the end this might be the best thing, like also from a Security point of view
|
||||
const user = await fetch('http://transcendance:8080/api/v2/user')
|
||||
.then((resp) => resp.json())
|
||||
|
||||
console.log('in /test what is in user')
|
||||
console.log(user)
|
||||
|
||||
if (user && user.username)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
],
|
||||
// props: {
|
||||
// user: user
|
||||
// }
|
||||
}),
|
||||
'/game': Game,
|
||||
'/matchlist': SpectatorMatchList,
|
||||
'/ranking' : Ranking,
|
||||
'/profile': wrap({
|
||||
component: ProfilePage,
|
||||
conditions: [
|
||||
@@ -179,82 +51,7 @@ export const primaryRoutes = {
|
||||
}
|
||||
]
|
||||
}),
|
||||
// '/game': wrap({
|
||||
// component: ProfilePage,
|
||||
// conditions: [
|
||||
// async(detail) => {
|
||||
// const user = await fetch('http://transcendance:8080/api/v2/user')
|
||||
// .then((resp) => resp.json())
|
||||
|
||||
// console.log('in /test what is in user')
|
||||
// console.log(user)
|
||||
|
||||
// if (user && user.username)
|
||||
// return true;
|
||||
// else
|
||||
// return false;
|
||||
// }
|
||||
// ]
|
||||
// }),
|
||||
// '/spectate': wrap({
|
||||
// component: ProfilePage,
|
||||
// conditions: [
|
||||
// async(detail) => {
|
||||
// const user = await fetch('http://transcendance:8080/api/v2/user')
|
||||
// .then((resp) => resp.json())
|
||||
|
||||
// console.log('in /test what is in user')
|
||||
// console.log(user)
|
||||
|
||||
// if (user && user.username)
|
||||
// return true;
|
||||
// else
|
||||
// return false;
|
||||
// }
|
||||
// ]
|
||||
// }),
|
||||
// '/chat': wrap({
|
||||
// component: ProfilePage,
|
||||
// conditions: [
|
||||
// async(detail) => {
|
||||
// const user = await fetch('http://transcendance:8080/api/v2/user')
|
||||
// .then((resp) => resp.json())
|
||||
|
||||
// console.log('in /test what is in user')
|
||||
// console.log(user)
|
||||
|
||||
// if (user && user.username)
|
||||
// return true;
|
||||
// else
|
||||
// return false;
|
||||
// }
|
||||
// ]
|
||||
// }),
|
||||
'/unauthorized-access': UnauthorizedAccessPage,
|
||||
'*': NotFound
|
||||
};
|
||||
|
||||
// export const primaryRoutes = {
|
||||
// "/": SplashPage,
|
||||
// "/profile": ProfilePage,
|
||||
// "/game": GamePage,
|
||||
// "/chat": ChatPage,
|
||||
// "*": NotFound
|
||||
// };
|
||||
|
||||
|
||||
// i might need to add /profile/* and such to make the nested routers work
|
||||
|
||||
// ok maybe these need to be in their own files?
|
||||
|
||||
|
||||
|
||||
// export const gameRoutes = {
|
||||
// "/": GamePage,
|
||||
// "*": NotFound
|
||||
// };
|
||||
|
||||
// export const chatRoutes = {
|
||||
// "/": ChatPage,
|
||||
// "*": NotFound
|
||||
// };
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"extends": "@tsconfig/svelte/tsconfig.json",
|
||||
|
||||
"include": ["src/**/*", "public/game"],
|
||||
"include": ["src/**/*", "public/game", "public/client"],
|
||||
"exclude": ["node_modules/*", "__sapper__/*", "public/*"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user