MISC: Add achievements for BN14 (#2140)
69
assets/Steam/achievements/icons/BN14+.svg
Normal file
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="256"
|
||||
height="256"
|
||||
viewBox="0 0 67.733325 67.733325"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.4 (86a8ad7, 2024-10-11)"
|
||||
sodipodi:docname="BN14+.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="true"
|
||||
units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:deskcolor="#d1d1d1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid1"
|
||||
originx="0"
|
||||
originy="0"
|
||||
spacingy="1"
|
||||
spacingx="1"
|
||||
units="px" />
|
||||
</sodipodi:namedview>
|
||||
<defs
|
||||
id="defs2" />
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer2"
|
||||
inkscape:label="background">
|
||||
<rect
|
||||
style="fill:#000000;stroke:none;stroke-width:7.02745"
|
||||
id="rect1"
|
||||
width="67.73333"
|
||||
height="67.73333"
|
||||
x="0"
|
||||
y="0" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:label="main"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:17.6389px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;text-align:center;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#55d400;stroke-width:0.264583"
|
||||
x="33.591061"
|
||||
y="40.205647"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:17.6389px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;text-anchor:middle;fill:#00ff00;fill-opacity:1;stroke-width:0.264583"
|
||||
x="33.591061"
|
||||
y="40.205647">BN14+</tspan></text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
69
assets/Steam/achievements/icons/SF14.1.svg
Normal file
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="256"
|
||||
height="256"
|
||||
viewBox="0 0 67.733325 67.733325"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.4 (86a8ad7, 2024-10-11)"
|
||||
sodipodi:docname="SF14.1.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="true"
|
||||
units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:deskcolor="#d1d1d1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid1"
|
||||
originx="0"
|
||||
originy="0"
|
||||
spacingy="1"
|
||||
spacingx="1"
|
||||
units="px" />
|
||||
</sodipodi:namedview>
|
||||
<defs
|
||||
id="defs2" />
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer2"
|
||||
inkscape:label="background">
|
||||
<rect
|
||||
style="fill:#000000;stroke:none;stroke-width:7.02745"
|
||||
id="rect1"
|
||||
width="67.73333"
|
||||
height="67.73333"
|
||||
x="0"
|
||||
y="0" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:label="main"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:17.6389px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;text-align:center;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#55d400;stroke-width:0.264583"
|
||||
x="34.982018"
|
||||
y="40.175503"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:17.6389px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;text-anchor:middle;fill:#00ff00;fill-opacity:1;stroke-width:0.264583"
|
||||
x="34.982018"
|
||||
y="40.175503">SF14.1</tspan></text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
74
assets/Steam/achievements/icons/ipvgo-anticheat.svg
Normal file
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="256"
|
||||
height="256"
|
||||
viewBox="0 0 67.733325 67.733325"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.4 (86a8ad7, 2024-10-11)"
|
||||
sodipodi:docname="ipvgo-anticheat.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="true"
|
||||
units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:deskcolor="#d1d1d1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid1"
|
||||
originx="0"
|
||||
originy="0"
|
||||
spacingy="1"
|
||||
spacingx="1"
|
||||
units="px" />
|
||||
</sodipodi:namedview>
|
||||
<defs
|
||||
id="defs2" />
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer2"
|
||||
inkscape:label="background">
|
||||
<rect
|
||||
style="fill:#000000;stroke:none;stroke-width:7.02745"
|
||||
id="rect1"
|
||||
width="67.73333"
|
||||
height="67.73333"
|
||||
x="0"
|
||||
y="0" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:label="main"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:13.4056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;text-align:center;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#55d400;stroke-width:0.264583"
|
||||
x="33.948486"
|
||||
y="30.28944"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:13.4056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;text-anchor:middle;fill:#00ff00;fill-opacity:1;stroke-width:0.264583"
|
||||
x="33.948486"
|
||||
y="30.28944">IPvGO</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:13.4056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;text-anchor:middle;fill:#00ff00;fill-opacity:1;stroke-width:0.264583"
|
||||
x="33.948486"
|
||||
y="47.04644"
|
||||
id="tspan2">Anticheat</tspan></text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
69
assets/Steam/achievements/icons/ipvgo-winning-streak.svg
Normal file
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="256"
|
||||
height="256"
|
||||
viewBox="0 0 67.733325 67.733325"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.4 (86a8ad7, 2024-10-11)"
|
||||
sodipodi:docname="ipvgo-winning-streak.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="true"
|
||||
units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:deskcolor="#d1d1d1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid1"
|
||||
originx="0"
|
||||
originy="0"
|
||||
spacingy="1"
|
||||
spacingx="1"
|
||||
units="px" />
|
||||
</sodipodi:namedview>
|
||||
<defs
|
||||
id="defs2" />
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer2"
|
||||
inkscape:label="background">
|
||||
<rect
|
||||
style="fill:#000000;stroke:none;stroke-width:7.02745"
|
||||
id="rect1"
|
||||
width="67.73333"
|
||||
height="67.73333"
|
||||
x="0"
|
||||
y="0" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:label="main"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:17.6389px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;text-align:center;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#55d400;stroke-width:0.264583"
|
||||
x="33.763317"
|
||||
y="40.097988"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:17.6389px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;text-anchor:middle;fill:#00ff00;fill-opacity:1;stroke-width:0.264583"
|
||||
x="33.763317"
|
||||
y="40.097988"
|
||||
id="tspan3">10 Dan</tspan></text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -56,13 +56,13 @@
|
||||
id="layer1">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:17.6389px;font-family:Arial;-inkscape-font-specification:Arial;text-align:center;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#55d400;stroke-width:0.264583"
|
||||
style="font-size:17.6389px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;text-align:center;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#55d400;stroke-width:0.264583"
|
||||
x="33.858055"
|
||||
y="40.17981"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:17.6389px;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;text-anchor:middle;fill:#00ff00;fill-opacity:1;stroke-width:0.264583"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:17.6389px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;text-anchor:middle;fill:#00ff00;fill-opacity:1;stroke-width:0.264583"
|
||||
x="33.858055"
|
||||
y="40.17981">TEXT</tspan></text>
|
||||
</g>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
69
dist/icons/achievements/BN14+.svg
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="256"
|
||||
height="256"
|
||||
viewBox="0 0 67.733325 67.733325"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.4 (86a8ad7, 2024-10-11)"
|
||||
sodipodi:docname="BN14+.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="true"
|
||||
units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:deskcolor="#d1d1d1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid1"
|
||||
originx="0"
|
||||
originy="0"
|
||||
spacingy="1"
|
||||
spacingx="1"
|
||||
units="px" />
|
||||
</sodipodi:namedview>
|
||||
<defs
|
||||
id="defs2" />
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer2"
|
||||
inkscape:label="background">
|
||||
<rect
|
||||
style="fill-opacity: 0%;stroke:none;stroke-width:7.02745"
|
||||
id="rect1"
|
||||
width="67.73333"
|
||||
height="67.73333"
|
||||
x="0"
|
||||
y="0" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:label="main"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:17.6389px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;text-align:center;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#55d400;stroke-width:0.264583"
|
||||
x="33.591061"
|
||||
y="40.205647"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:17.6389px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke-width:0.264583"
|
||||
x="33.591061"
|
||||
y="40.205647">BN14+</tspan></text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
69
dist/icons/achievements/SF14.1.svg
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="256"
|
||||
height="256"
|
||||
viewBox="0 0 67.733325 67.733325"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.4 (86a8ad7, 2024-10-11)"
|
||||
sodipodi:docname="SF14.1.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="true"
|
||||
units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:deskcolor="#d1d1d1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid1"
|
||||
originx="0"
|
||||
originy="0"
|
||||
spacingy="1"
|
||||
spacingx="1"
|
||||
units="px" />
|
||||
</sodipodi:namedview>
|
||||
<defs
|
||||
id="defs2" />
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer2"
|
||||
inkscape:label="background">
|
||||
<rect
|
||||
style="fill-opacity: 0%;stroke:none;stroke-width:7.02745"
|
||||
id="rect1"
|
||||
width="67.73333"
|
||||
height="67.73333"
|
||||
x="0"
|
||||
y="0" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:label="main"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:17.6389px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;text-align:center;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#55d400;stroke-width:0.264583"
|
||||
x="34.982018"
|
||||
y="40.175503"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:17.6389px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke-width:0.264583"
|
||||
x="34.982018"
|
||||
y="40.175503">SF14.1</tspan></text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
74
dist/icons/achievements/ipvgo-anticheat.svg
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="256"
|
||||
height="256"
|
||||
viewBox="0 0 67.733325 67.733325"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.4 (86a8ad7, 2024-10-11)"
|
||||
sodipodi:docname="ipvgo-anticheat.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="true"
|
||||
units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:deskcolor="#d1d1d1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid1"
|
||||
originx="0"
|
||||
originy="0"
|
||||
spacingy="1"
|
||||
spacingx="1"
|
||||
units="px" />
|
||||
</sodipodi:namedview>
|
||||
<defs
|
||||
id="defs2" />
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer2"
|
||||
inkscape:label="background">
|
||||
<rect
|
||||
style="fill-opacity: 0%;stroke:none;stroke-width:7.02745"
|
||||
id="rect1"
|
||||
width="67.73333"
|
||||
height="67.73333"
|
||||
x="0"
|
||||
y="0" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:label="main"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:13.4056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;text-align:center;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#55d400;stroke-width:0.264583"
|
||||
x="33.948486"
|
||||
y="30.28944"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:13.4056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke-width:0.264583"
|
||||
x="33.948486"
|
||||
y="30.28944">IPvGO</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:13.4056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke-width:0.264583"
|
||||
x="33.948486"
|
||||
y="47.04644"
|
||||
id="tspan2">Anticheat</tspan></text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
69
dist/icons/achievements/ipvgo-winning-streak.svg
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="256"
|
||||
height="256"
|
||||
viewBox="0 0 67.733325 67.733325"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.4 (86a8ad7, 2024-10-11)"
|
||||
sodipodi:docname="ipvgo-winning-streak.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="true"
|
||||
units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:deskcolor="#d1d1d1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid1"
|
||||
originx="0"
|
||||
originy="0"
|
||||
spacingy="1"
|
||||
spacingx="1"
|
||||
units="px" />
|
||||
</sodipodi:namedview>
|
||||
<defs
|
||||
id="defs2" />
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer2"
|
||||
inkscape:label="background">
|
||||
<rect
|
||||
style="fill-opacity: 0%;stroke:none;stroke-width:7.02745"
|
||||
id="rect1"
|
||||
width="67.73333"
|
||||
height="67.73333"
|
||||
x="0"
|
||||
y="0" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:label="main"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:17.6389px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial;text-align:center;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#55d400;stroke-width:0.264583"
|
||||
x="33.763317"
|
||||
y="40.097988"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:17.6389px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke-width:0.264583"
|
||||
x="33.763317"
|
||||
y="40.097988"
|
||||
id="tspan3">10 Dan</tspan></text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -131,6 +131,11 @@
|
||||
"Name": "They're lunatics",
|
||||
"Description": "Acquire SF13.1"
|
||||
},
|
||||
"SF14.1": {
|
||||
"ID": "SF14.1",
|
||||
"Name": "IPvGO Subnet Takeover",
|
||||
"Description": "Acquire SF14.1"
|
||||
},
|
||||
"MONEY_1Q": {
|
||||
"ID": "MONEY_1Q",
|
||||
"Name": "Here comes the money!",
|
||||
@@ -391,6 +396,16 @@
|
||||
"Name": "More BitNodes, please!",
|
||||
"Description": "Reach SF x.3 in each BitNode."
|
||||
},
|
||||
"IPVGO_ANTICHEAT": {
|
||||
"ID": "IPVGO_ANTICHEAT",
|
||||
"Name": "IPvGO anticheat",
|
||||
"Description": "Fail to cheat an IPvGO game and be ejected from the subnet."
|
||||
},
|
||||
"IPVGO_WINNING_STREAK": {
|
||||
"ID": "IPVGO_WINNING_STREAK",
|
||||
"Name": "Ten Steps Ahead",
|
||||
"Description": "Get a winning streak of 10 against Illuminati."
|
||||
},
|
||||
"CHALLENGE_BN1": {
|
||||
"ID": "CHALLENGE_BN1",
|
||||
"Name": "BN1: Challenge",
|
||||
@@ -436,6 +451,16 @@
|
||||
"Name": "BN12: Challenge",
|
||||
"Description": "Destroy BN12 50 times."
|
||||
},
|
||||
"CHALLENGE_BN13": {
|
||||
"ID": "CHALLENGE_BN13",
|
||||
"Name": "BN13: Challenge",
|
||||
"Description": "Complete BN13 without Stanek's Gift."
|
||||
},
|
||||
"CHALLENGE_BN14": {
|
||||
"ID": "CHALLENGE_BN14",
|
||||
"Name": "BN14: Challenge",
|
||||
"Description": "Destroy BN14 without making a move or cheating via IPvGO APIs."
|
||||
},
|
||||
"BYPASS": {
|
||||
"ID": "BYPASS",
|
||||
"Name": "Exploit: bypass",
|
||||
@@ -481,11 +506,6 @@
|
||||
"Name": "UNACHIEVABLE",
|
||||
"Description": "This achievement cannot be unlocked."
|
||||
},
|
||||
"CHALLENGE_BN13": {
|
||||
"ID": "CHALLENGE_BN13",
|
||||
"Name": "BN13: Challenge",
|
||||
"Description": "Complete BN13 without Stanek's Gift."
|
||||
},
|
||||
"DEVMENU": {
|
||||
"ID": "DEVMENU",
|
||||
"Name": "Exploit: you're not meant to access this",
|
||||
|
||||
@@ -29,16 +29,51 @@ import { workerScripts } from "../Netscript/WorkerScripts";
|
||||
|
||||
import { getRecordValues } from "../Types/Record";
|
||||
import { ServerConstants } from "../Server/data/Constants";
|
||||
import { canAccessBitNodeFeature, isBitNodeFinished, knowAboutBitverse, validBitNodes } from "../BitNode/BitNodeUtils";
|
||||
import { canAccessBitNodeFeature, isBitNodeFinished, knowAboutBitverse } from "../BitNode/BitNodeUtils";
|
||||
import { validBitNodes } from "../BitNode/Constants";
|
||||
import { isLegacyScript } from "../Paths/ScriptFilePath";
|
||||
import { Settings } from "../Settings/Settings";
|
||||
import { activateSteamAchievements } from "../Electron";
|
||||
import { Go } from "../Go/Go";
|
||||
import { type AchievementId, type SFAchievementId, SFAchievementIds } from "./Types";
|
||||
|
||||
// Unable to correctly cast the JSON data into AchievementDataJson type otherwise...
|
||||
const achievementData = (<AchievementDataJson>(<unknown>data)).achievements;
|
||||
function assertAchievements(
|
||||
achievements: typeof data.achievements,
|
||||
): asserts achievements is AchievementDataJson["achievements"] {
|
||||
for (const [key, value] of Object.entries(achievements)) {
|
||||
if (key !== value.ID) {
|
||||
throw new Error(`Invalid achievement ID. Key: ${key}. Value: ${value.ID}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of data.achievements is:
|
||||
{
|
||||
CYBERSEC: {
|
||||
ID: string;
|
||||
Name: string;
|
||||
Description: string;
|
||||
};
|
||||
NITESEC: {
|
||||
ID: string;
|
||||
Name: string;
|
||||
Description: string;
|
||||
};
|
||||
...
|
||||
}
|
||||
* However, we want:
|
||||
* - Typechecking at compile time: ID must be AchievementId, not string.
|
||||
* - Runtime check: The value of ID must be the same as the key of the achievement. For example, with "CYBERSEC"
|
||||
* achievement, the key is "CYBERSEC", so its ID must also be "CYBERSEC".
|
||||
*
|
||||
* We use assertAchievements to do the runtime check and assert the type.
|
||||
*/
|
||||
const achievementData = data.achievements;
|
||||
assertAchievements(achievementData);
|
||||
|
||||
export interface Achievement {
|
||||
ID: string;
|
||||
ID: AchievementId;
|
||||
Icon?: string;
|
||||
Name?: string;
|
||||
Description?: string;
|
||||
@@ -46,48 +81,59 @@ export interface Achievement {
|
||||
NotInSteam?: boolean;
|
||||
Condition: () => boolean;
|
||||
Visible?: () => boolean;
|
||||
AdditionalUnlock?: string[]; // IDs of achievements that should be awarded when awarding this one
|
||||
AdditionalUnlock?: AchievementId[]; // IDs of achievements that should be awarded when awarding this one
|
||||
}
|
||||
|
||||
export interface PlayerAchievement {
|
||||
ID: string;
|
||||
ID: AchievementId;
|
||||
unlockedOn?: number;
|
||||
}
|
||||
|
||||
export interface AchievementDataJson {
|
||||
achievements: Record<string, AchievementData>;
|
||||
achievements: Record<AchievementId, AchievementData>;
|
||||
}
|
||||
|
||||
export interface AchievementData {
|
||||
ID: string;
|
||||
ID: AchievementId;
|
||||
Name: string;
|
||||
Description: string;
|
||||
}
|
||||
|
||||
function sfAchievements(): Record<string, Achievement> {
|
||||
const achievements: Record<string, Achievement> = {};
|
||||
for (let i = 1; i <= 13; i++) {
|
||||
const ID = `SF${i}.1`;
|
||||
achievements[ID] = {
|
||||
...achievementData[ID],
|
||||
Icon: ID,
|
||||
function sfAchievements(): Record<SFAchievementId, Achievement> {
|
||||
const achievements = {} as Record<SFAchievementId, Achievement>;
|
||||
for (const id of SFAchievementIds) {
|
||||
const matchResult = id.match(/SF(\d{1,2})\.1/);
|
||||
if (!matchResult) {
|
||||
throw new Error(`Unexpected SFAchievementId: ${id}`);
|
||||
}
|
||||
const bn = Number.parseInt(matchResult[1]);
|
||||
if (!validBitNodes.includes(bn)) {
|
||||
throw new Error(`Unexpected BN value in SFAchievementId: ${id}`);
|
||||
}
|
||||
achievements[id] = {
|
||||
/**
|
||||
* The type of achievementData is still the original type (CYBERSEC: { ID: string; Name: string; Description: string; }).
|
||||
* We have to typecast it here.
|
||||
*/
|
||||
...(achievementData as AchievementDataJson["achievements"])[id],
|
||||
Icon: id,
|
||||
Visible: knowAboutBitverse,
|
||||
Condition: () => Player.sourceFileLvl(i) >= 1,
|
||||
NotInSteam: i >= 13,
|
||||
Condition: () => Player.sourceFileLvl(bn) >= 1,
|
||||
NotInSteam: bn >= 13,
|
||||
};
|
||||
}
|
||||
return achievements;
|
||||
}
|
||||
|
||||
export const achievements: Record<string, Achievement> = {
|
||||
[FactionName.CyberSec.toUpperCase()]: {
|
||||
...achievementData[FactionName.CyberSec.toUpperCase()],
|
||||
export const achievements: Record<AchievementId, Achievement> = {
|
||||
CYBERSEC: {
|
||||
...achievementData.CYBERSEC,
|
||||
Icon: "CSEC",
|
||||
Condition: () => Player.factions.includes(FactionName.CyberSec),
|
||||
},
|
||||
[FactionName.NiteSec.toUpperCase()]: {
|
||||
...achievementData[FactionName.NiteSec.toUpperCase()],
|
||||
Icon: FactionName.NiteSec,
|
||||
NITESEC: {
|
||||
...achievementData.NITESEC,
|
||||
Icon: "NiteSec",
|
||||
Condition: () => Player.factions.includes(FactionName.NiteSec),
|
||||
},
|
||||
THE_BLACK_HAND: {
|
||||
@@ -95,24 +141,24 @@ export const achievements: Record<string, Achievement> = {
|
||||
Icon: "TBH",
|
||||
Condition: () => Player.factions.includes(FactionName.TheBlackHand),
|
||||
},
|
||||
[FactionName.BitRunners.toUpperCase()]: {
|
||||
...achievementData[FactionName.BitRunners.toUpperCase()],
|
||||
Icon: FactionName.BitRunners.toLowerCase(),
|
||||
BITRUNNERS: {
|
||||
...achievementData.BITRUNNERS,
|
||||
Icon: "bitrunners",
|
||||
Condition: () => Player.factions.includes(FactionName.BitRunners),
|
||||
},
|
||||
[FactionName.Daedalus.toUpperCase()]: {
|
||||
...achievementData[FactionName.Daedalus.toUpperCase()],
|
||||
Icon: FactionName.Daedalus.toLowerCase(),
|
||||
DAEDALUS: {
|
||||
...achievementData.DAEDALUS,
|
||||
Icon: "daedalus",
|
||||
Condition: () => Player.factions.includes(FactionName.Daedalus),
|
||||
},
|
||||
THE_COVENANT: {
|
||||
...achievementData.THE_COVENANT,
|
||||
Icon: FactionName.TheCovenant.toLowerCase().replace(/ /g, ""),
|
||||
Icon: "thecovenant",
|
||||
Condition: () => Player.factions.includes(FactionName.TheCovenant),
|
||||
},
|
||||
[FactionName.Illuminati.toUpperCase()]: {
|
||||
...achievementData[FactionName.Illuminati.toUpperCase()],
|
||||
Icon: FactionName.Illuminati.toLowerCase(),
|
||||
ILLUMINATI: {
|
||||
...achievementData.ILLUMINATI,
|
||||
Icon: "illuminati",
|
||||
Condition: () => Player.factions.includes(FactionName.Illuminati),
|
||||
},
|
||||
"BRUTESSH.EXE": {
|
||||
@@ -522,6 +568,20 @@ export const achievements: Record<string, Achievement> = {
|
||||
Condition: () => validBitNodes.every((bn) => Player.sourceFileLvl(bn) >= 3),
|
||||
NotInSteam: true,
|
||||
},
|
||||
IPVGO_ANTICHEAT: {
|
||||
...achievementData.IPVGO_ANTICHEAT,
|
||||
Icon: "ipvgo-anticheat",
|
||||
Visible: knowAboutBitverse,
|
||||
Condition: () => false,
|
||||
NotInSteam: true,
|
||||
},
|
||||
IPVGO_WINNING_STREAK: {
|
||||
...achievementData.IPVGO_WINNING_STREAK,
|
||||
Icon: "ipvgo-winning-streak",
|
||||
Visible: knowAboutBitverse,
|
||||
Condition: () => false,
|
||||
NotInSteam: true,
|
||||
},
|
||||
CHALLENGE_BN1: {
|
||||
...achievementData.CHALLENGE_BN1,
|
||||
Icon: "BN1+",
|
||||
@@ -596,6 +656,22 @@ export const achievements: Record<string, Achievement> = {
|
||||
Visible: () => canAccessBitNodeFeature(12),
|
||||
Condition: () => Player.sourceFileLvl(12) >= 50,
|
||||
},
|
||||
CHALLENGE_BN13: {
|
||||
...achievementData.CHALLENGE_BN13,
|
||||
Icon: "BN13+",
|
||||
Visible: () => canAccessBitNodeFeature(13),
|
||||
Condition: () =>
|
||||
Player.bitNodeN === 13 &&
|
||||
isBitNodeFinished() &&
|
||||
!Player.augmentations.some((a) => a.name === AugmentationName.StaneksGift1),
|
||||
},
|
||||
CHALLENGE_BN14: {
|
||||
...achievementData.CHALLENGE_BN14,
|
||||
Icon: "BN14+",
|
||||
Visible: knowAboutBitverse,
|
||||
Condition: () => Player.bitNodeN === 14 && isBitNodeFinished() && !Go.moveOrCheatViaApi,
|
||||
NotInSteam: true,
|
||||
},
|
||||
BYPASS: {
|
||||
...achievementData.BYPASS,
|
||||
Icon: "SF-1",
|
||||
@@ -651,15 +727,6 @@ export const achievements: Record<string, Achievement> = {
|
||||
// Hey Players! Yes, you're supposed to modify this to get the achievement!
|
||||
Condition: () => false,
|
||||
},
|
||||
CHALLENGE_BN13: {
|
||||
...achievementData.CHALLENGE_BN13,
|
||||
Icon: "BN13+",
|
||||
Visible: () => canAccessBitNodeFeature(13),
|
||||
Condition: () =>
|
||||
Player.bitNodeN === 13 &&
|
||||
isBitNodeFinished() &&
|
||||
!Player.augmentations.some((a) => a.name === AugmentationName.StaneksGift1),
|
||||
},
|
||||
DEVMENU: {
|
||||
...achievementData.DEVMENU,
|
||||
Icon: "SF-1",
|
||||
|
||||
@@ -23,5 +23,7 @@ Steps:
|
||||
- Match the order of achievements in `AchievementData.json`.
|
||||
- `Icon` must be the name of the .svg file.
|
||||
- `NotInSteam` must be true.
|
||||
- Run `pack-for-web.sh`.
|
||||
- Run `pack-for-web.sh` in `$PROJECT_DIR/assets/Steam/achievements`.
|
||||
- When committing, remember to commit the changes in `$PROJECT_DIR/dist/icons/achievements`.
|
||||
|
||||
Note: If you add a new SFx.1 achievement, you must add its ID to `SFAchievementIds` in `$PROJECT_DIR/src/Achievements/Types.ts`.
|
||||
|
||||
20
src/Achievements/Types.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import data from "./AchievementData.json";
|
||||
|
||||
export type AchievementId = keyof typeof data.achievements;
|
||||
export const SFAchievementIds = [
|
||||
"SF1.1",
|
||||
"SF2.1",
|
||||
"SF3.1",
|
||||
"SF4.1",
|
||||
"SF5.1",
|
||||
"SF6.1",
|
||||
"SF7.1",
|
||||
"SF8.1",
|
||||
"SF9.1",
|
||||
"SF10.1",
|
||||
"SF11.1",
|
||||
"SF12.1",
|
||||
"SF13.1",
|
||||
"SF14.1",
|
||||
] as const;
|
||||
export type SFAchievementId = (typeof SFAchievementIds)[number];
|
||||
@@ -4,8 +4,7 @@ import { GetServer } from "../Server/AllServers";
|
||||
import { Server } from "../Server/Server";
|
||||
import { SpecialServers } from "../Server/data/SpecialServers";
|
||||
import { JSONMap } from "../Types/Jsonable";
|
||||
|
||||
export const validBitNodes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
|
||||
import { validBitNodes } from "./Constants";
|
||||
|
||||
export function isBitNodeFinished(): boolean {
|
||||
const wd = GetServer(SpecialServers.WorldDaemon);
|
||||
|
||||
1
src/BitNode/Constants.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const validBitNodes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
|
||||
@@ -14,11 +14,12 @@ import LockOpenIcon from "@mui/icons-material/LockOpen";
|
||||
import { Player } from "@player";
|
||||
import { achievements } from "../../Achievements/Achievements";
|
||||
import { Engine } from "../../engine";
|
||||
import type { AchievementId } from "../../Achievements/Types";
|
||||
|
||||
export function AchievementsDev(): React.ReactElement {
|
||||
const [playerAchievement, setPlayerAchievements] = useState(Player.achievements.map((m) => m.ID));
|
||||
|
||||
function grantAchievement(id: string): void {
|
||||
function grantAchievement(id: AchievementId): void {
|
||||
Player.giveAchievement(id);
|
||||
setPlayerAchievements(Player.achievements.map((m) => m.ID));
|
||||
}
|
||||
@@ -28,7 +29,7 @@ export function AchievementsDev(): React.ReactElement {
|
||||
setPlayerAchievements(Player.achievements.map((m) => m.ID));
|
||||
}
|
||||
|
||||
function removeAchievement(id: string): void {
|
||||
function removeAchievement(id: AchievementId): void {
|
||||
Player.achievements = Player.achievements.filter((a) => a.ID !== id);
|
||||
setPlayerAchievements(Player.achievements.map((m) => m.ID));
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ export class GoObject {
|
||||
currentGame: BoardState = getNewBoardState(7);
|
||||
stats: PartialRecord<GoOpponent, OpponentStats> = {};
|
||||
storedCycles: number = 0;
|
||||
// This flag is used when checking the achievement CHALLENGE_BN14.
|
||||
moveOrCheatViaApi = false;
|
||||
|
||||
prestigeAugmentation() {
|
||||
for (const opponent of getRecordKeys(Go.stats)) {
|
||||
@@ -36,6 +38,7 @@ export class GoObject {
|
||||
this.previousGame = null;
|
||||
this.currentGame = getNewBoardState(7);
|
||||
this.stats = {};
|
||||
this.moveOrCheatViaApi = false;
|
||||
resetGoPromises();
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ type SaveFormat = {
|
||||
currentGame: CurrentGameSaveData;
|
||||
stats: PartialRecord<GoOpponent, OpponentStats>;
|
||||
storedCycles: number;
|
||||
moveOrCheatViaApi: boolean;
|
||||
};
|
||||
|
||||
export function getGoSave(): SaveFormat {
|
||||
@@ -46,6 +47,7 @@ export function getGoSave(): SaveFormat {
|
||||
},
|
||||
stats: Go.stats,
|
||||
storedCycles: Go.storedCycles,
|
||||
moveOrCheatViaApi: Go.moveOrCheatViaApi,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -85,6 +87,9 @@ export function loadGo(data: unknown): boolean {
|
||||
Go.previousGame = previousGame;
|
||||
Go.stats = stats;
|
||||
Go.storeCycles(loadStoredCycles(parsedData.storedCycles));
|
||||
if (typeof parsedData.moveOrCheatViaApi === "boolean") {
|
||||
Go.moveOrCheatViaApi = parsedData.moveOrCheatViaApi;
|
||||
}
|
||||
|
||||
resetAI();
|
||||
handleNextTurn(currentGame).catch((error) => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Board, BoardState, PointState } from "../Types";
|
||||
|
||||
import { Player } from "@player";
|
||||
import { GoOpponent, GoColor } from "@enums";
|
||||
import { GoOpponent, GoColor, FactionName } from "@enums";
|
||||
import { newOpponentStats } from "../Constants";
|
||||
import { getAllChains, getPlayerNeighbors } from "./boardAnalysis";
|
||||
import { getKomi, resetAI } from "./goAI";
|
||||
@@ -77,6 +77,10 @@ export function endGoGame(boardState: BoardState) {
|
||||
Factions[factionName].setFavor(newFavor);
|
||||
statusToUpdate.rep += repToAdd;
|
||||
}
|
||||
|
||||
if (factionName === FactionName.Illuminati && statusToUpdate.winStreak >= 10) {
|
||||
Player.giveAchievement("IPVGO_WINNING_STREAK");
|
||||
}
|
||||
}
|
||||
|
||||
statusToUpdate.nodePower +=
|
||||
|
||||
@@ -29,6 +29,7 @@ import { newOpponentStats } from "../Constants";
|
||||
* Check the move based on the current settings
|
||||
*/
|
||||
export function validateMove(error: (s: string) => never, x: number, y: number, methodName = "", settings = {}): void {
|
||||
Go.moveOrCheatViaApi = true;
|
||||
const check = {
|
||||
emptyNode: true,
|
||||
requireNonEmptyNode: false,
|
||||
@@ -502,10 +503,11 @@ export function determineCheatSuccess(
|
||||
if ((successRngOverride ?? rng.random()) <= cheatSuccessChance(state.cheatCount, playAsWhite)) {
|
||||
callback();
|
||||
}
|
||||
// If there have been prior cheat attempts, and the cheat fails, there is a 10% chance of instantly losing
|
||||
// If there have been prior cheat attempts, and the cheat fails, there is a 10% chance of instantly ending the game
|
||||
else if (priorCheatCount && (ejectRngOverride ?? rng.random()) < 0.1 && state.ai !== GoOpponent.none) {
|
||||
logger(`Cheat failed! You have been ejected from the subnet.`);
|
||||
forceEndGoGame(state);
|
||||
Player.giveAchievement("IPVGO_ANTICHEAT");
|
||||
return handleNextTurn(state, true);
|
||||
} else {
|
||||
// If the cheat fails, your turn is skipped
|
||||
|
||||
@@ -108,7 +108,8 @@ import { ServerConstants } from "./Server/data/Constants";
|
||||
import { assertFunctionWithNSContext } from "./Netscript/TypeAssertion";
|
||||
import { Router } from "./ui/GameRoot";
|
||||
import { Page } from "./ui/Router";
|
||||
import { canAccessBitNodeFeature, validBitNodes } from "./BitNode/BitNodeUtils";
|
||||
import { canAccessBitNodeFeature } from "./BitNode/BitNodeUtils";
|
||||
import { validBitNodes } from "./BitNode/Constants";
|
||||
import { isIPAddress } from "./Types/strings";
|
||||
import { compile } from "./NetscriptJSEvaluator";
|
||||
import { Script } from "./Script/Script";
|
||||
|
||||
@@ -48,7 +48,7 @@ import { ServerConstants } from "../Server/data/Constants";
|
||||
import { blackOpsArray } from "../Bladeburner/data/BlackOperations";
|
||||
import { calculateEffectiveRequiredReputation } from "../Company/utils";
|
||||
import { addRepToFavor } from "../Faction/formulas/favor";
|
||||
import { validBitNodes } from "../BitNode/BitNodeUtils";
|
||||
import { validBitNodes } from "../BitNode/Constants";
|
||||
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
||||
import { cat } from "../Terminal/commands/cat";
|
||||
import { Crimes } from "../Crime/Crimes";
|
||||
|
||||
@@ -54,6 +54,7 @@ import { AlertEvents } from "../../ui/React/AlertManager";
|
||||
import { Augmentations } from "../../Augmentation/Augmentations";
|
||||
import { PlayerEventType, PlayerEvents } from "./PlayerEvents";
|
||||
import { Result } from "../../types";
|
||||
import type { AchievementId } from "../../Achievements/Types";
|
||||
|
||||
export function init(this: PlayerObject): void {
|
||||
/* Initialize Player's home computer */
|
||||
@@ -561,9 +562,11 @@ export function giveExploit(this: PlayerObject, exploit: Exploit): void {
|
||||
}
|
||||
}
|
||||
|
||||
export function giveAchievement(this: PlayerObject, achievementId: string): void {
|
||||
export function giveAchievement(this: PlayerObject, achievementId: AchievementId): void {
|
||||
const achievement = achievements[achievementId];
|
||||
if (!achievement) return;
|
||||
if (!achievement) {
|
||||
return;
|
||||
}
|
||||
if (!this.achievements.map((a) => a.ID).includes(achievementId)) {
|
||||
this.achievements.push({ ID: achievementId, unlockedOn: new Date().getTime() });
|
||||
SnackbarEvents.emit(`Unlocked Achievement: "${achievement.Name}"`, ToastVariant.SUCCESS, 2000);
|
||||
|
||||
@@ -45,6 +45,7 @@ exports[`Check Save File Continuity GoSave continuity 1`] = `
|
||||
"previousBoard": "",
|
||||
"previousPlayer": "White",
|
||||
},
|
||||
"moveOrCheatViaApi": false,
|
||||
"previousGame": null,
|
||||
"stats": {},
|
||||
"storedCycles": 0,
|
||||
|
||||