Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cartridge Controller integration #350

Closed
wants to merge 146 commits into from
Closed
Changes from all commits
Commits
Show all changes
146 commits
Select commit Hold shift + click to select a range
6f55cc3
update to dojo 0.4.2
notV4l Dec 21, 2023
164321c
update to dojo 0.4.2
notV4l Dec 21, 2023
39010ca
0.5.0
notV4l Jan 19, 2024
db514b2
v0.5.0 with local dojo.js packages
notV4l Jan 19, 2024
a1975d6
temp
notV4l Jan 17, 2024
bdb622d
market packed
notV4l Jan 23, 2024
a4ced99
fix build issues & cleaning
notV4l Jan 23, 2024
c64be6c
fix bug infinte load crash
notV4l Jan 24, 2024
887b0dc
remove hood_id
notV4l Jan 24, 2024
92b7ca2
temp
notV4l Jan 24, 2024
c09272f
remove start_time
notV4l Jan 24, 2024
b14a759
remove max_players
notV4l Jan 24, 2024
b013054
remove num_players
notV4l Jan 24, 2024
71e5c32
remove legacy tests
notV4l Jan 24, 2024
e4a3b55
fix build
notV4l Jan 24, 2024
3460f74
remove creator
notV4l Jan 24, 2024
2703188
cleaning
notV4l Jan 24, 2024
d5eaccd
wip, cleaning & refactoring & experiments
notV4l Jan 25, 2024
2107839
refactoring
notV4l Jan 26, 2024
be64325
stores in DojoContext
notV4l Jan 26, 2024
1c5b333
remove externals
notV4l Jan 26, 2024
8afa47b
format
notV4l Jan 26, 2024
672a63b
moving config into models
notV4l Jan 26, 2024
531665f
load config from torii
notV4l Jan 29, 2024
8fe5639
useRouterContext
notV4l Jan 29, 2024
bfdd979
start using configStore
notV4l Jan 30, 2024
60e7adb
cleaning
notV4l Jan 30, 2024
fdcb74a
packed state WIP
notV4l Jan 31, 2024
cf95424
wip
notV4l Feb 2, 2024
3cb4d02
mega commit
notV4l Feb 2, 2024
30709b4
gas opti
notV4l Feb 3, 2024
2bfac86
trade & wanted simple
notV4l Feb 5, 2024
40f57cb
better market & shop
notV4l Feb 6, 2024
fc8b8a2
trade & shop & travel
notV4l Feb 8, 2024
22527e9
cleaning & fix some build issues
notV4l Feb 8, 2024
4cb0d01
pawnshop
notV4l Feb 9, 2024
88a47a5
add hustlers icons
notV4l Feb 9, 2024
2543902
basic encounters
notV4l Feb 9, 2024
c6369d4
wip encounters & events
notV4l Feb 12, 2024
87ee10b
wip decide & events
notV4l Feb 14, 2024
c9673e0
limit shopping
notV4l Feb 14, 2024
2f6c316
almost basic game loop
notV4l Feb 14, 2024
153389a
leaderboard & cleaning
notV4l Feb 16, 2024
12916e5
make it build..
notV4l Feb 16, 2024
14ba092
deploy
notV4l Feb 16, 2024
d1d8f6f
fix
notV4l Feb 16, 2024
ff69a3e
with music
notV4l Feb 16, 2024
1e55350
v0.6.0-alpha.0
notV4l Feb 20, 2024
ae4e4ba
hustler items wip
notV4l Feb 21, 2024
2710d7d
hustler items
notV4l Feb 22, 2024
cb22f08
jailed & hospitalized
notV4l Feb 22, 2024
b190a5a
wanted
notV4l Feb 23, 2024
b902043
drug progression
notV4l Feb 23, 2024
03a5d7e
ogstuff
notV4l Feb 23, 2024
646e518
tuning
notV4l Feb 26, 2024
ac2f24b
add Connect
notV4l Feb 27, 2024
c8cb647
v0.6.0-alpha.2
notV4l Feb 27, 2024
6589b63
update powermeter
notV4l Feb 27, 2024
c96e460
wip
notV4l Feb 27, 2024
7e6c345
reorg mods & fmt
notV4l Feb 28, 2024
ee9bbde
paper stuff
notV4l Feb 28, 2024
975747e
name by game
notV4l Mar 1, 2024
89bb103
reorg components
notV4l Mar 1, 2024
3f87d1d
hustler profile
notV4l Mar 1, 2024
d7602f7
deploy test version
notV4l Mar 1, 2024
5f95bf6
temp provider
notV4l Mar 1, 2024
6425dfd
bump version 0.6.0-alpha.3
notV4l Mar 4, 2024
43735a4
fix logic
notV4l Mar 4, 2024
c037269
fix payout & tune
notV4l Mar 4, 2024
7d32196
loadout
notV4l Mar 4, 2024
fe51dc2
better map
notV4l Mar 4, 2024
b4fe25c
ui changes
notV4l Mar 4, 2024
fa3a278
hall of fame & stuff
notV4l Mar 5, 2024
d93453f
basic claim
notV4l Mar 6, 2024
0a76ca5
redeploy
notV4l Mar 6, 2024
0a2f39b
fix spectator logs
notV4l Mar 6, 2024
6d21ee0
starknetjs 6.2.0
notV4l Mar 7, 2024
9404591
encounter images
notV4l Mar 7, 2024
887bd06
ui update
notV4l Mar 7, 2024
bed8a2c
remove unecessary stuff
notV4l Mar 7, 2024
8fd6d9a
reputation ranks
notV4l Mar 7, 2024
bab2f2e
add too_poor_to_get_rekt
notV4l Mar 7, 2024
f39bfd7
fixes
notV4l Mar 7, 2024
42f478c
adustments
notV4l Mar 8, 2024
3617d65
wip
notV4l Mar 11, 2024
ce8ec9f
wip
notV4l Mar 12, 2024
e355212
fix build issues
notV4l Mar 12, 2024
7f0ba07
genesis experiment
notV4l Mar 13, 2024
5e83754
add claim button & modal
notV4l Mar 13, 2024
be26662
entry_fee & tokenBalance
notV4l Mar 13, 2024
ba10b19
test with github branch ref
notV4l Mar 13, 2024
6acbdff
full uri
notV4l Mar 13, 2024
b23ba93
full uri
notV4l Mar 13, 2024
0f9e219
smol uri
notV4l Mar 13, 2024
5713404
rank
notV4l Mar 14, 2024
f0d8331
more drugs, more fun
notV4l Mar 14, 2024
b59d2ae
shuffle_drug_prices after level up
notV4l Mar 14, 2024
25f645b
fix build erros
notV4l Mar 15, 2024
557d02c
wip
notV4l Mar 20, 2024
cd01dcc
make it build
notV4l Mar 21, 2024
9e03e3c
building
notV4l Mar 21, 2024
2d356c6
wip
notV4l Mar 22, 2024
2c658f9
420 & 421
notV4l Mar 23, 2024
ac882ee
small ui adjustements
notV4l Mar 25, 2024
72ec9d6
update drug images
notV4l Mar 25, 2024
f01a6b7
simple admin ui
notV4l Mar 25, 2024
4e13166
with predeployed connector
notV4l Mar 26, 2024
5516eb5
better error handling
notV4l Mar 29, 2024
a0b0070
deployed
notV4l Mar 29, 2024
abd0439
temp: alpha10
notV4l Apr 1, 2024
22ce394
reputation
notV4l Apr 1, 2024
97bf792
change table / admin
notV4l Apr 2, 2024
753ebe2
add table icons
notV4l Apr 2, 2024
cac5882
new encounters wip
notV4l Apr 3, 2024
1a4f0b8
fixes
notV4l Apr 3, 2024
45a8c33
0.6.0
notV4l Apr 8, 2024
5f2d26b
base encounter admin
notV4l Apr 9, 2024
4c7af86
deployed
notV4l Apr 9, 2024
cb6b454
analytics -> speedInsights
notV4l Apr 10, 2024
da99e69
ui fixes
notV4l Apr 10, 2024
4f9f157
dojo.js dependency
notV4l Apr 10, 2024
ef6c86f
us-east
notV4l Apr 10, 2024
041bd43
Threat matrix UI changes (#322)
satyambnsal Apr 11, 2024
1927e4d
new encounters wip
notV4l Apr 11, 2024
aa2168b
new encounters wip
notV4l Apr 12, 2024
1bb2048
ui fixes
notV4l Apr 13, 2024
23b3bb7
ui fixes
notV4l Apr 13, 2024
c9dc5e9
encounter admin & fixes
notV4l Apr 14, 2024
b2a656e
v0.6.1-alpha.1
notV4l Apr 14, 2024
4697db8
v0.6.1-alpha.1
notV4l Apr 14, 2024
7143796
redeploy
notV4l Apr 15, 2024
ad8ba86
redeploy
notV4l Apr 15, 2024
f98f444
rermove dope-mock
notV4l Apr 30, 2024
71fbba7
basic game history
notV4l Apr 30, 2024
c80a860
cops & gang names
notV4l Apr 30, 2024
4ddd07c
force green katana icon
notV4l Apr 30, 2024
74af146
update images
notV4l Apr 30, 2024
329524f
fix build issue
notV4l Apr 30, 2024
adb32de
update scripts
notV4l Apr 30, 2024
c764424
query all game infos
notV4l Apr 30, 2024
474060d
fix manifest
notV4l Apr 30, 2024
8357db0
v0.7.0-alpha.0
notV4l May 2, 2024
7515de3
redeploy
notV4l May 13, 2024
96a8337
hide warrior
notV4l May 13, 2024
1af5689
[WIP] Add cartridge connector
JunichiSugiura May 16, 2024
ef4b3c6
Replace all connectors with Cartridge Controller
JunichiSugiura May 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -22,9 +22,8 @@ jobs:
sudo apt-get install -y curl
- name: Download Dojo release artifact
# curl -L -o dojo-linux-x86_64.tar.gz https://github.com/dojoengine/dojo/releases/download/nightly/dojo_nightly_linux_amd64.tar.gz
run: |
curl -L -o dojo-linux-x86_64.tar.gz https://github.com/dojoengine/dojo/releases/download/v0.2.2/dojo_v0.2.2_linux_amd64.tar.gz
curl -L -o dojo-linux-x86_64.tar.gz https://github.com/dojoengine/dojo/releases/download/v0.5.0/dojo_v0.5.0_linux_amd64.tar.gz
tar -xzf dojo-linux-x86_64.tar.gz
sudo mv sozo /usr/local/bin/
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
target
target
abis

/manifests
13 changes: 11 additions & 2 deletions Scarb.lock
Original file line number Diff line number Diff line change
@@ -3,8 +3,8 @@ version = 1

[[package]]
name = "dojo"
version = "0.3.15"
source = "git+https://github.com/dojoengine/dojo.git?tag=v0.3.15#cdbaca4121b1642e1a8ca40d6831bad73d28ed26"
version = "0.6.0"
source = "git+https://github.com/dojoengine/dojo?tag=v0.7.0-alpha.0#2b2dc1bf35e4568bda7341f17267a931b49148b0"
dependencies = [
"dojo_plugin",
]
@@ -17,6 +17,15 @@ source = "git+https://github.com/dojoengine/dojo?tag=v0.3.11#1e651b5d4d3b79b14a7
[[package]]
name = "rollyourown"
version = "0.1.0"
dependencies = [
"dojo",
"token",
]

[[package]]
name = "token"
version = "0.0.0"
source = "git+https://github.com/dojoengine/origami?tag=v0.7.0-alpha.0#53c06b6a32d0d715a265b2a5d290f333b8c000f9"
dependencies = [
"dojo",
]
85 changes: 63 additions & 22 deletions Scarb.toml
Original file line number Diff line number Diff line change
@@ -1,45 +1,76 @@
[package]
cairo-version = "2.3.0"
cairo-version = "2.5.4"
name = "rollyourown"
version = "0.1.0"

[cairo]
sierra-replace-ids = true

[dependencies]
# cubit = {git = "https://github.com/influenceth/cubit.git"}
dojo = {git = "https://github.com/dojoengine/dojo.git", tag = "v0.3.15"}
# dojo = {git = "https://github.com/dojoengine/dojo.git", rev = "d7f46502cba3d6462b68a4dfb07336377bca2678"}
dojo = {git = "https://github.com/dojoengine/dojo", tag = "v0.7.0-alpha.0"}
token = { git = "https://github.com/dojoengine/origami", tag = "v0.7.0-alpha.0"}

[[target.dojo]]

[scripts]
katana = "katana --disable-fee --invoke-max-steps 999999999"

#
katana = "katana --disable-fee --invoke-max-steps 2000000"
build = "sozo build && scarb run gendojo"

# common
copy_manifest = "./scripts/copy_manifest.sh"
gendojo = "./scripts/gen.sh"
auth = "./scripts/default_auth.sh dev"
auth_staging = "./scripts/default_auth.sh staging"
auth_prod = "./scripts/default_auth.sh prod"
migrate = "sozo -P dev migrate && scarb run gendojo && scarb run auth"
migrate_staging = "sozo -P staging migrate"
# migrate_staging = "sozo migrate --rpc-url https://api.cartridge.gg/x/ryodev/katana --account-address 0x6e857786bbd1652857d673836c41e0544d9d5ecd3e7a1bbde744e328b8cc2f6 --private-key 0x5e9d85de083b7ddd3029c44de2997ceba8384074bbebb66696a1b507f0466fc"
migrate_prod = "sozo -P prod migrate"
auth = "./scripts/default_auth.sh"


# migrate
migrate = "PROFILE=dev && sozo -P $PROFILE build && sozo -P $PROFILE migrate apply && scarb run copy_manifest $PROFILE && scarb run gendojo $PROFILE && scarb run auth $PROFILE"
migrate_ryo420 = "PROFILE=ryo420 && sozo -P $PROFILE build && sozo -P $PROFILE migrate apply && scarb run copy_manifest $PROFILE && scarb run gendojo $PROFILE && scarb run auth $PROFILE"
# migrate_ryo421 = "PROFILE=ryo421 && sozo -P $PROFILE build && sozo -P $PROFILE migrate apply && scarb run copy_manifest $PROFILE && scarb run gendojo $PROFILE && scarb run auth $PROFILE"

migrateplan_sepolia = "PROFILE=ryosepolia && sozo -P $PROFILE build && sozo -P $PROFILE migrate plan --name ryo000"
migrate_sepolia = "PROFILE=ryosepolia && sozo -P $PROFILE build && sozo -P $PROFILE migrate apply --name ryo000 --fee-estimate-multiplier 5 && scarb run copy_manifest $PROFILE"
auth_sepolia = "PROFILE=ryosepolia && scarb run auth $PROFILE"

# slot ryo420
slot_ryo420_katana="slot d create ryo420 katana --seed 420 --version v0.7.0-alpha.0 --chain-id KATANA_SLOT_420 --disable-fee true --invoke-max-steps 2000000"
slot_ryo420_torii="slot d create ryo420 torii --rpc https://api.cartridge.gg/x/ryo420/katana -s 0 --version v0.7.0-alpha.0 --world 0x3bf84ccc82282acd4c8afbb843c9e864bf1e0770fba607595104202b938b7a4"

# # slot ryo421
# slot_ryo421_katana="slot d create ryo421 katana --seed 421 --version v0.7.0-alpha.0 --chain-id KATANA_SLOT_421 --disable-fee true --invoke-max-steps 2000000"
# slot_ryo421_torii="slot d create ryo421 torii --rpc https://api.cartridge.gg/x/ryo421/katana -s 0 --version v0.7.0-alpha.0 --world 0x3bf84ccc82282acd4c8afbb843c9e864bf1e0770fba607595104202b938b7a4"

# slot ryo_sepolia
slot_ryosepolia_torii="slot d create ryosepolia torii --rpc https://api.cartridge.gg/rpc/starknet-sepolia -s 63200 --version v0.7.0-alpha.0 --world 0x45d3b72e3c6c85c38d43798b9751817f14fac4b14c3645480683f8fae1c6464"
# slot_ryosepolia_torii="slot d create ryosepolia torii --rpc https://free-rpc.nethermind.io/sepolia-juno -s 63260 --version v0.7.0-alpha.0 --world 0x45d3b72e3c6c85c38d43798b9751817f14fac4b14c3645480683f8fae1c6464"


[profile.dev.tool.dojo.env]
rpc_url = "http://localhost:5050"
account_address = "0x517ececd29116499f4a1b64b094da79ba08dfd54a3edaa316134c41f8160973"
account_address = "0x6162896d1d7ab204c7ccac6dd5f8e9e7c25ecd5ae4fcb4ad32e57786bb46e03"
private_key = "0x1800000000300000180000000000030000000000003006001800006600"

# seed 420
# [profile.staging.tool.dojo.env]
# rpc_url = "https://api.cartridge.gg/x/ryodev/katana"
# account_address = "0x6e857786bbd1652857d673836c41e0544d9d5ecd3e7a1bbde744e328b8cc2f6"
# private_key = "0x5e9d85de083b7ddd3029c44de2997ceba8384074bbebb66696a1b507f0466fc"
[profile.ryo420.tool.dojo.env]
rpc_url = "https://api.cartridge.gg/x/ryo420/katana"
account_address = "0x754d8bc62099e306ab40deb98accc3e717eb1a7b8838060c6312c6e8f8ee1d7"
private_key = "0x2f9a2435c3195dfa3c2f8290de5347e0da48193fd6d6d80320f0201a0964b8c"

# [profile.ryo421.tool.dojo.env]
# rpc_url = "https://api.cartridge.gg/x/ryo421/katana"
# account_address = "0x7d806fc9478c73c60fac37c27888771bdb3092c21eb93452277e7673954d034"
# private_key = "0x784b1dd14d761c414c6394fccca3ca1d1b0cac187e88122e4b06378f9e8c515"

[profile.ryosepolia.tool.dojo.env]
# world_address= "0x45d3b72e3c6c85c38d43798b9751817f14fac4b14c3645480683f8fae1c6464"
rpc_url = "https://api.cartridge.gg/rpc/starknet-sepolia"
account_address = "0x3677d8443f74dcc6cd23c4b3f217256c70f084ee7edc4ddc431af2ce91eb936"
keystore_path = "/Users/boo/sozo_acc"
# password = "sozo_acc"

# [profile.prod.tool.dojo.env]
# rpc_url = "https://api.cartridge.gg/x/ryo/katana"
# account_address = "0x6e857786bbd1652857d673836c41e0544d9d5ecd3e7a1bbde744e328b8cc2f6"
# private_key = "0x5e9d85de083b7ddd3029c44de2997ceba8384074bbebb66696a1b507f0466fc"
# rpc_url = "https://api.cartridge.gg/x/ryo420/katana"
# account_address = "0x795abc2a2d5866f75c58025741329973db20966d1add5dd2a9fbdf0bb8a0266"
# private_key = "0x2e8ac99614186737cefc47effe03134f5a19c6dc2443c16510d3384769f9c78"

[tool.dojo.world]
name = "Roll Your Own"
@@ -49,3 +80,13 @@ icon_uri = "file://assets/icon.png"
cover_uri = "file://assets/cover.png"
socials.x = "https://x.com/TheDopeWars"


[[target.dojo]]
build-external-contracts = [
"token::components::security::initializable::initializable_model",
"token::components::token::erc20::erc20_metadata::erc_20_metadata_model",
"token::components::token::erc20::erc20_balance::erc_20_balance_model",
"token::components::token::erc20::erc20_allowance::erc_20_allowance_model",
"token::components::token::erc20::erc20_mintable::erc_20_mintable_model",
"token::components::token::erc20::erc20_burnable::erc_20_burnable_model",
]
4,579 changes: 4,579 additions & 0 deletions genesis/compiled/account.json

Large diffs are not rendered by default.

48,343 changes: 48,343 additions & 0 deletions genesis/compiled/erc20.json

Large diffs are not rendered by default.

4,390 changes: 4,390 additions & 0 deletions genesis/compiled/oz_account_080.json

Large diffs are not rendered by default.

7,037 changes: 7,037 additions & 0 deletions genesis/compiled/universal_deployer.json

Large diffs are not rendered by default.

226 changes: 226 additions & 0 deletions genesis/generateGenesis.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
const fs = require("fs");
const path = require("path");

const { RpcProvider } = require("starknet");

const provider = new RpcProvider({ nodeUrl: "http://localhost:5050" })

const manifest = require("../target/dev/manifest.json")

const getStateUpdates = async () => {
const lastBlock = await provider.getBlockNumber()
console.log("lastBlock", lastBlock)

const updates = []
for (let i = 0; i <= lastBlock; i++) {
console.log("block:", i)
const update = await provider.getBlockStateUpdate(i)
updates.push(update)
}
return updates
}


const main = async () => {

// const updates = await getStateUpdates()
// fs.writeFileSync(path.resolve('./updates.json'), JSON.stringify(updates,null,2))

const updates = JSON.parse(fs.readFileSync(path.resolve('./updates.json')))

const genesis = generateGenesis(updates)

fs.writeFileSync(path.resolve('./genesis.json'), JSON.stringify(genesis, null, 2))

}



const generateGenesis = (updates) => {

const genesis = genesisInitialState

for (let update of updates) {

parseDeclaredClasses(genesis, update.state_diff.declared_classes)
parseDeployedContracts(genesis, update.state_diff.deployed_contracts)

parseStorageDiff(genesis, update.state_diff.storage_diffs)
parseNonces(genesis, update.state_diff.nonces)

}

return genesis
}

/****************************************************************/


const parseStorageDiff = (genesis, diffs) => {
for (let diff of diffs) {
let contract = genesis.contracts[diff.address]
contract = contract || genesis.accounts[diff.address]

if (!contract) {
console.log("contract not found :", diff.address)
}

for (let entry of diff.storage_entries) {
contract.storage[entry.key] = entry.value
}
}
}

/****************************************************************/


const parseNonces = (genesis, nonces) => {

for (let nonce of nonces) {
const account = genesis.accounts[nonce.contract_address]
if (account) {
account.nonce = nonce.nonce
continue
}

// check if account is in contract //and move to account
const contract = genesis.contracts[nonce.contract_address]
if (contract) {
// genesis.accounts[nonce.contract_address] = contract
// delete genesis.contracts[nonce.contract_address]
// console.log("moved contract -> account")
contract.nonce = nonce.nonce
}
}
}

/****************************************************************/

const parseDeployedContracts = (genesis, deployedContracts) => {

for (let deployedContract of deployedContracts) {
// check if account
if (genesis.accounts[deployedContract.address]) {

genesis.accounts[deployedContract.address] = {
...genesis.accounts[deployedContract.address],
storage: {}
}

} else {
genesis.contracts[deployedContract.address] = {
// balance:"0x0",
class: deployedContract.class_hash,
storage: {}
}
}

}

}

/****************************************************************/

const parseDeclaredClasses = (genesis, declaredClasses) => {
for (let declaredClass of declaredClasses) {

//check if already in
if (genesis.classes.find(i => BigInt(i.classHash) === BigInt(declaredClass.class_hash))) { continue }

genesis.classes.push({
class: getClassArtifactFromClassHash(declaredClass.class_hash),
classHash: declaredClass.class_hash
})
}
}


const getArtifactPath = (name) => `../target/dev/${name}.json`

const getClassArtifactFromClassHash = (classHash) => {
let isBase = manifest.base.class_hash === classHash
if (isBase) {
return getArtifactPath(manifest.base.name)
}

let isWorld = manifest.world.class_hash === classHash
if (isWorld) {
return getArtifactPath(manifest.world.name)
}

let contract = manifest.contracts.find(i => i.class_hash === classHash)
if (contract) {
return getArtifactPath(contract.name)
}

let model = manifest.models.find(i => i.class_hash === classHash)
if (model) {
return getArtifactPath(model.name)
}

return "not found :("

}




const genesisInitialState = {
"number": 0,
"parentHash": "0x0",
"timestamp": 5123512314,
"stateRoot": "0x0",
"sequencerAddress": "0x100",
"gasPrices": {
"ETH": 1111,
"STRK": 2222
},
"feeToken": {
"address": "0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
"name": "ETHER",
"symbol": "ETH",
"decimals": 18,
"class": "0x02a8846878b6ad1f54f6ba46f5f40e11cee755c677f130b2c4b60566c9003f1f",
"storage": {}
},
"universalDeployer": {
"address": "0x041a78e741e5af2fec34b695679bc6891742439f7afb8484ecd7766661ad02bf",
"storage": {}
},
"accounts": {
"0xb3ff441a68610b30fd5e2abbf3a1548eb6ba6f3559f2862bf2dc757e5828ca": {
"publicKey": "0x640466ebd2ce505209d3e5c4494b4276ed8f1cde764d757eb48831961f7cdea",
"balance": "0xD3C21BCECCEDA1000000",
"nonce": "0x0",
"class": "0x05400e90f7e0ae78bd02c77cd75527280470e2fe19c54970dd79dc37a9d3645c",
"storage": {}
},
},
"contracts": {
// "0x29873c310fbefde666dc32a1554fea6bb45eecc84f680f8a2b0a8fbb8cb89af": {
// "balance": "0xD3C21BCECCEDA1000000",
// "class": "0x8",
// "storage": {
// "0x1": "0x1",
// "0x2": "0x2"
// }
// },
},
"classes": [
{
"class": "./compiled/erc20.json",
"classHash": "0x02a8846878b6ad1f54f6ba46f5f40e11cee755c677f130b2c4b60566c9003f1f"
},
{
"class": "./compiled/universal_deployer.json",
"classHash": "0x07b3e05f48f0c69e4a65ce5e076a66271a527aff2c34ce1083ec6e1526997a69"
},
{
"class": "./compiled/account.json",
"classHash": "0x05400e90f7e0ae78bd02c77cd75527280470e2fe19c54970dd79dc37a9d3645c"
}
]
}


main()
458 changes: 458 additions & 0 deletions genesis/genesis.json

Large diffs are not rendered by default.

1,822 changes: 1,822 additions & 0 deletions genesis/updates.json

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions scripts/copy_manifest.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash
set -euo pipefail

if [ $# -ge 1 ]; then
export PROFILE=$1
else
export PROFILE="dev"
fi

mkdir -p ./web/src/manifests/$PROFILE
cp ./manifests/$PROFILE/manifest.json ./web/src/manifests/$PROFILE/manifest.json


114 changes: 60 additions & 54 deletions scripts/default_auth.sh
Original file line number Diff line number Diff line change
@@ -8,69 +8,75 @@ else
export PROFILE="dev"
fi

export WORLD_ADDRESS=$(cat ./target/dev/manifest.json | jq -r '.world.address')
TX_SLEEP=1

export LOBBY_ADDRESS=$(cat ./target/dev/manifest.json | jq -r '.contracts[] | select(.name == "lobby" ).address')
export TRAVEL_ADDRESS=$(cat ./target/dev/manifest.json | jq -r '.contracts[] | select(.name == "travel" ).address')
export DECIDE_ADDRESS=$(cat ./target/dev/manifest.json | jq -r '.contracts[] | select(.name == "decide" ).address')
export TRADE_ADDRESS=$(cat ./target/dev/manifest.json | jq -r '.contracts[] | select(.name == "trade" ).address')
export SHOP_ADDRESS=$(cat ./target/dev/manifest.json | jq -r '.contracts[] | select(.name == "shop" ).address')
export RYO_ADDRESS=$(cat ./target/dev/manifest.json | jq -r '.contracts[] | select(.name == "ryo" ).address')
export WORLD_ADDRESS=$(cat ./manifests/$PROFILE/manifest.json | jq -r '.world.address')

export RYO_ADDRESS=$(cat ./manifests/$PROFILE/manifest.json | jq -r '.contracts[] | select(.name == "rollyourown::systems::ryo::ryo" ).address')
export CONFIG_ADDRESS=$(cat ./manifests/$PROFILE/manifest.json | jq -r '.contracts[] | select(.name == "rollyourown::config::config::config" ).address')
export GAME_ADDRESS=$(cat ./manifests/$PROFILE/manifest.json | jq -r '.contracts[] | select(.name == "rollyourown::systems::game::game" ).address')

export PAPER_MOCK_ADDRESS=$(cat ./manifests/$PROFILE/manifest.json | jq -r '.contracts[] | select(.name == "rollyourown::_mocks::paper_mock::paper_mock" ).address')

# dev/katana
# export TREASURY_ADDRESS="0xe29882a1fcba1e7e10cad46212257fea5c752a4f9b1b1ec683c503a2cf5c8a";

# sepolia deployer
export TREASURY_ADDRESS="0x3677d8443f74dcc6cd23c4b3f217256c70f084ee7edc4ddc431af2ce91eb936";

echo "---------------------------------------------------------------------------"
echo profile : $PROFILE
echo "---------------------------------------------------------------------------"
echo world : $WORLD_ADDRESS
echo " "
echo lobby : $LOBBY_ADDRESS
echo travel: $TRAVEL_ADDRESS
echo decide: $DECIDE_ADDRESS
echo trade : $TRADE_ADDRESS
echo shop : $SHOP_ADDRESS
echo ryo : $RYO_ADDRESS
echo world : $WORLD_ADDRESS
echo "---------------------------------------------------------------------------"
echo ryo : $RYO_ADDRESS
echo config : $CONFIG_ADDRESS
echo game : $GAME_ADDRESS
echo paper : $PAPER_MOCK_ADDRESS
echo "---------------------------------------------------------------------------"

# enable system -> component authorizations
LOBBY_COMPONENTS=("Game" "Market" "Player" "Leaderboard" "RyoMeta")
TRAVEL_COMPONENTS=("Player" "Market" "Encounter" "Leaderboard" "RyoMeta")
DECIDE_COMPONENTS=("Player" "Drug" "Market" "Encounter" "Leaderboard" "RyoMeta")
TRADE_COMPONENTS=("Drug" "Market" "Player")
SHOP_COMPONENTS=("Player" "Item" "Market")
RYO_COMPONENTS=("RyoMeta" "Leaderboard")

for component in ${LOBBY_COMPONENTS[@]}; do
sozo -P $PROFILE auth writer $component $LOBBY_ADDRESS --world $WORLD_ADDRESS
sleep 0.1
done

for component in ${TRAVEL_COMPONENTS[@]}; do
sozo -P $PROFILE auth writer $component $TRAVEL_ADDRESS --world $WORLD_ADDRESS
sleep 0.1
done

for component in ${DECIDE_COMPONENTS[@]}; do
sozo -P $PROFILE auth writer $component $DECIDE_ADDRESS --world $WORLD_ADDRESS
sleep 0.1
done

for component in ${TRADE_COMPONENTS[@]}; do
sozo -P $PROFILE auth writer $component $TRADE_ADDRESS --world $WORLD_ADDRESS
sleep 0.1
done

for component in ${SHOP_COMPONENTS[@]}; do
sozo -P $PROFILE auth writer $component $SHOP_ADDRESS --world $WORLD_ADDRESS
sleep 0.1
done

for component in ${RYO_COMPONENTS[@]}; do
sozo -P $PROFILE auth writer $component $RYO_ADDRESS --world $WORLD_ADDRESS
sleep 0.1
done

# enable system -> models authorizations
sozo -P $PROFILE auth grant --world $WORLD_ADDRESS --fee-estimate-multiplier 5 --wait writer\
RyoConfig,$RYO_ADDRESS \
RyoAddress,$RYO_ADDRESS \
Leaderboard,$RYO_ADDRESS \
GameConfig,$CONFIG_ADDRESS \
DrugConfig,$CONFIG_ADDRESS \
LocationConfig,$CONFIG_ADDRESS \
HustlerItemBaseConfig,$CONFIG_ADDRESS \
HustlerItemTiersConfig,$CONFIG_ADDRESS \
EncounterConfig,$CONFIG_ADDRESS \
Game,$GAME_ADDRESS \
GameStorePacked,$GAME_ADDRESS \
RyoConfig,$GAME_ADDRESS \
Leaderboard,$GAME_ADDRESS \


# remove later
sozo -P $PROFILE auth grant --world $WORLD_ADDRESS --wait writer\
ERC20MetadataModel,$PAPER_MOCK_ADDRESS \
ERC20BalanceModel,$PAPER_MOCK_ADDRESS \
ERC20AllowanceModel,$PAPER_MOCK_ADDRESS \
InitializableModel,$PAPER_MOCK_ADDRESS \


echo "Default authorizations have been successfully set."

echo "Initializing..."
sozo -P $PROFILE execute $RYO_ADDRESS initialize
echo "Initialized!"
sozo -P $PROFILE execute --world $WORLD_ADDRESS $RYO_ADDRESS initialize --calldata $PAPER_MOCK_ADDRESS,$TREASURY_ADDRESS --wait
echo "Initialized RYO!"
sleep $TX_SLEEP

sozo -P $PROFILE execute --world $WORLD_ADDRESS $CONFIG_ADDRESS initialize_1 --wait
echo "Initialized CONFIG 1!"
sleep $TX_SLEEP

sozo -P $PROFILE execute --world $WORLD_ADDRESS $CONFIG_ADDRESS initialize_2 --wait
echo "Initialized CONFIG 2!"
sleep $TX_SLEEP

# remove later
sozo -P $PROFILE execute --world $WORLD_ADDRESS $PAPER_MOCK_ADDRESS initializer --wait
echo "Initialized PAPER_MOCK!"
sleep $TX_SLEEP
13 changes: 10 additions & 3 deletions scripts/gen.sh
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
#!/bin/bash
set -euo pipefail

cp ./target/dev/manifest.json ./web/manifest.json
if [ $# -ge 1 ]; then
export PROFILE=$1
else
export PROFILE="dev"
fi

npx abi-wan-kanabi@2.2.0 --input ./target/$PROFILE/rollyourown::config::config::config.json --output ./web/src/dojo/abis/configAbi.ts
npx abi-wan-kanabi@2.2.0 --input ./target/$PROFILE/rollyourown::_mocks::paper_mock::paper_mock.json --output ./web/src/dojo/abis/paperAbi.ts

pushd $(dirname "$0")/../web

yarn run gen:dojo
prettier --write ./src/dojo/generated
pnpm run gen:dojo
pnpm exec prettier --write ./src/dojo/generated
9 changes: 0 additions & 9 deletions scripts/rename.sh

This file was deleted.

191 changes: 191 additions & 0 deletions src/_mocks/paper_mock.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
use starknet::{ContractAddress, ClassHash};
use dojo::world::IWorldDispatcher;

#[starknet::interface]
trait IPaperMock<TState> {
// IWorldProvider
fn world(self: @TState,) -> IWorldDispatcher;

// IUpgradeable
fn upgrade(ref self: TState, new_class_hash: ClassHash);

// IERC20Metadata
fn decimals(self: @TState,) -> u8;
fn name(self: @TState,) -> felt252;
fn symbol(self: @TState,) -> felt252;

// IERC20MetadataTotalSupply
fn total_supply(self: @TState,) -> u256;

// IERC20MetadataTotalSupplyCamel
fn totalSupply(self: @TState,) -> u256;

// IERC20Balance
fn balance_of(self: @TState, account: ContractAddress) -> u256;
fn transfer(ref self: TState, recipient: ContractAddress, amount: u256) -> bool;
fn transfer_from(
ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256
) -> bool;

// IERC20BalanceCamel
fn balanceOf(self: @TState, account: ContractAddress) -> u256;
fn transferFrom(
ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256
) -> bool;

// IERC20Allowance
fn allowance(self: @TState, owner: ContractAddress, spender: ContractAddress) -> u256;
fn approve(ref self: TState, spender: ContractAddress, amount: u256) -> bool;

// WITHOUT INTERFACE !!!
fn initializer(ref self: TState);
fn dojo_resource(self: @TState,) -> felt252;
}


///
/// Interface required to remove compiler warnings and future
/// deprecation.
///
#[starknet::interface]
trait IPaperMockInitializer<TState> {
fn initializer(ref self: TState);
}

#[starknet::interface]
trait IPaperMockFaucet<TState> {
fn faucet(ref self: TState,);
fn faucetTo(ref self: TState, recipient: ContractAddress);
}


#[dojo::contract(allow_ref_self)]
mod paper_mock {
use integer::BoundedInt;
use starknet::ContractAddress;
use starknet::{get_caller_address, get_contract_address};
use zeroable::Zeroable;

use token::components::security::initializable::initializable_component;

use token::components::token::erc20::erc20_metadata::erc20_metadata_component;
use token::components::token::erc20::erc20_balance::erc20_balance_component;
use token::components::token::erc20::erc20_allowance::erc20_allowance_component;
use token::components::token::erc20::erc20_mintable::erc20_mintable_component;
use token::components::token::erc20::erc20_burnable::erc20_burnable_component;

component!(path: initializable_component, storage: initializable, event: InitializableEvent);

component!(path: erc20_metadata_component, storage: erc20_metadata, event: ERC20MetadataEvent);
component!(path: erc20_balance_component, storage: erc20_balance, event: ERC20BalanceEvent);
component!(
path: erc20_allowance_component, storage: erc20_allowance, event: ERC20AllowanceEvent
);
component!(path: erc20_mintable_component, storage: erc20_mintable, event: ERC20MintableEvent);
component!(path: erc20_burnable_component, storage: erc20_burnable, event: ERC20BurnableEvent);


#[storage]
struct Storage {
#[substorage(v0)]
initializable: initializable_component::Storage,
#[substorage(v0)]
erc20_metadata: erc20_metadata_component::Storage,
#[substorage(v0)]
erc20_balance: erc20_balance_component::Storage,
#[substorage(v0)]
erc20_allowance: erc20_allowance_component::Storage,
#[substorage(v0)]
erc20_mintable: erc20_mintable_component::Storage,
#[substorage(v0)]
erc20_burnable: erc20_burnable_component::Storage,
}

#[event]
#[derive(Copy, Drop, starknet::Event)]
enum Event {
InitializableEvent: initializable_component::Event,
ERC20MetadataEvent: erc20_metadata_component::Event,
ERC20BalanceEvent: erc20_balance_component::Event,
ERC20AllowanceEvent: erc20_allowance_component::Event,
ERC20MintableEvent: erc20_mintable_component::Event,
ERC20BurnableEvent: erc20_burnable_component::Event,
}

mod Errors {
const CALLER_IS_NOT_OWNER: felt252 = 'ERC20: caller is not owner';
}


impl InitializableImpl = initializable_component::InitializableImpl<ContractState>;

#[abi(embed_v0)]
impl ERC20MetadataImpl =
erc20_metadata_component::ERC20MetadataImpl<ContractState>;

#[abi(embed_v0)]
impl ERC20MetadataTotalSupplyImpl =
erc20_metadata_component::ERC20MetadataTotalSupplyImpl<ContractState>;

#[abi(embed_v0)]
impl ERC20MetadataTotalSupplyCamelImpl =
erc20_metadata_component::ERC20MetadataTotalSupplyCamelImpl<ContractState>;

#[abi(embed_v0)]
impl ERC20BalanceImpl =
erc20_balance_component::ERC20BalanceImpl<ContractState>;

#[abi(embed_v0)]
impl ERC20BalanceCamelImpl =
erc20_balance_component::ERC20BalanceCamelImpl<ContractState>;

#[abi(embed_v0)]
impl ERC20AllowanceImpl =
erc20_allowance_component::ERC20AllowanceImpl<ContractState>;

//
// Internal Impls
//

impl InitializableInternalImpl = initializable_component::InternalImpl<ContractState>;
impl ERC20MetadataInternalImpl = erc20_metadata_component::InternalImpl<ContractState>;
impl ERC20BalanceInternalImpl = erc20_balance_component::InternalImpl<ContractState>;
impl ERC20AllowanceInternalImpl = erc20_allowance_component::InternalImpl<ContractState>;
impl ERC20MintableInternalImpl = erc20_mintable_component::InternalImpl<ContractState>;
impl ERC20BurnableInternalImpl = erc20_burnable_component::InternalImpl<ContractState>;

//
// Initializer
//

#[abi(embed_v0)]
impl PaperMockInitializerImpl of super::IPaperMockInitializer<ContractState> {
fn initializer(ref self: ContractState) {
assert(
self.world().is_owner(get_caller_address(), get_contract_address().into()),
Errors::CALLER_IS_NOT_OWNER
);

self.erc20_metadata.initialize('fPAPER', 'fPAPER', 18);
self.erc20_mintable.mint(get_caller_address(), 10_000);

self.initializable.initialize();
}
}

//
// Faucet
//

const ETHER: u256 = 1_000_000_000_000_000_000;

#[abi(embed_v0)]
impl PaperMockFaucetImpl of super::IPaperMockFaucet<ContractState> {
fn faucet(ref self: ContractState) {
self.erc20_mintable.mint(get_caller_address(), 10_000 * ETHER);
}
fn faucetTo(ref self: ContractState, recipient: ContractAddress) {
self.erc20_mintable.mint(recipient, 10_000 * ETHER);
}
}
}
228 changes: 228 additions & 0 deletions src/config/config.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
use rollyourown::config::{
hustlers::{HustlerConfig, HustlerImpl},
game::{GameConfig}, drugs::{DrugConfig}, encounters::{EncounterConfig},
};


#[starknet::interface]
trait IConfig<T> {
fn initialize_1(self: @T);
fn initialize_2(self: @T);
fn get_config(self: @T) -> Config;
fn update_game_config(self: @T, game_config: GameConfig);
fn update_drug_config(self: @T, drug_config: DrugConfig);
fn update_encounter_config(self: @T, encounter_config: EncounterConfig);


}

#[derive(Drop, Serde)]
struct Config {
layouts: LayoutsConfig,
hustlers: Array<HustlerConfig>,
game_config: GameConfig, // TODO: query torii instead
}

#[derive(Drop, Serde)]
struct LayoutsConfig {
game_store: Array<LayoutItem>,
player: Array<LayoutItem>,
}


#[derive(Copy, Drop, Serde)]
struct LayoutItem {
name: bytes31,
idx: u8,
bits: u8,
}


#[dojo::contract]
mod config {
use starknet::{get_caller_address, get_contract_address};

use rollyourown::{
config::{
game::{initialize_game_config, GameConfig, GameConfigImpl},
drugs::{initialize_drug_config, DrugConfig, Drugs}, locations::initialize_location_config,
hustlers::{
HustlerConfig, HustlerImpl, initialize_weapons_config, initialize_clothes_config,
initialize_feet_config, initialize_transport_config, initialize_weapons_tiers_config,
initialize_clothes_tiers_config, initialize_feet_tiers_config,
initialize_transport_tiers_config,
},
encounters::{initialize_encounter_config,initialize_encounter_config_extra, EncounterConfig, Encounters},
},
packing::{
game_store_layout::{
GameStoreLayout, GameStoreLayoutEnumerableImpl, GameStoreLayoutPackableImpl,
GameStoreLayoutIntoBytes31Impl
},
player_layout::{
PlayerLayout, PlayerLayoutEnumerableImpl, PlayerLayoutPackableImpl,
PlayerLayoutIntoBytes31Impl
}
}
};

use super::{Config, LayoutsConfig, LayoutItem};

#[abi(embed_v0)]
impl ConfigImpl of super::IConfig<ContractState> {
fn initialize_1(self: @ContractState) {
// TODO checks
self.assert_caller_is_owner();

let world = self.world();

// common
initialize_game_config(world); // must be set before encounters

initialize_drug_config(world);
initialize_location_config(world);

// hustlers items
initialize_weapons_config(world);
initialize_clothes_config(world);
initialize_feet_config(world);
initialize_transport_config(world);

// hutlsers items tiers
initialize_weapons_tiers_config(world);
initialize_clothes_tiers_config(world);
initialize_feet_tiers_config(world);
initialize_transport_tiers_config(world);

}

fn initialize_2(self: @ContractState) {
// TODO checks
self.assert_caller_is_owner();

let world = self.world();

// encounters
initialize_encounter_config(world);
initialize_encounter_config_extra(world);

}

fn get_config(self: @ContractState) -> Config {
let world = self.world();

let mut game_store: Array<LayoutItem> = array![];
let mut game_store_layout_items = GameStoreLayoutEnumerableImpl::all();

loop {
match game_store_layout_items.pop_front() {
Option::Some(i) => {
game_store
.append(
LayoutItem { name: (*i).into(), bits: i.bits(), idx: i.idx(), }
);
},
Option::None => { break; }
};
};

//

let mut player: Array<LayoutItem> = array![];
let mut player_layout_items = PlayerLayoutEnumerableImpl::all();

loop {
match player_layout_items.pop_front() {
Option::Some(i) => {
player
.append(
LayoutItem { name: (*i).into(), bits: i.bits(), idx: i.idx(), }
);
},
Option::None => { break; }
};
};

//

let mut hustlers: Array<HustlerConfig> = array![];
let mut hustler_ids = array![0_u16, 1_u16, 2_u16].span();

loop {
match hustler_ids.pop_front() {
Option::Some(id) => {
let hustler = HustlerImpl::get(world, *id);
hustlers.append(hustler.get_hustler_config());
},
Option::None => { break; }
};
};

//
// TODO: remove & use torii
let game_config = GameConfigImpl::get(world);

//
Config { game_config, hustlers, layouts: LayoutsConfig { game_store, player } }
}

fn update_game_config(self: @ContractState, game_config: GameConfig) {
self.assert_caller_is_owner();
GameConfigImpl::set(self.world(), game_config);
}


fn update_drug_config(self: @ContractState, drug_config: DrugConfig) {
self.assert_caller_is_owner();

let drug: Drugs = drug_config.drug_id.into();
let mut to_update = get!(self.world(), (drug), (DrugConfig));

to_update.base = drug_config.base;
to_update.step = drug_config.step;
to_update.weight = drug_config.weight;
to_update.name = drug_config.name;

set!(self.world(), (to_update));
}

fn update_encounter_config(self: @ContractState, encounter_config: EncounterConfig) {
self.assert_caller_is_owner();

let mut to_update = get!(self.world(), (encounter_config.id), (EncounterConfig));

to_update.encounter = encounter_config.encounter;

to_update.level = encounter_config.level;
to_update.health = encounter_config.health;
to_update.attack = encounter_config.attack;
to_update.defense = encounter_config.defense;
to_update.speed = encounter_config.speed;

to_update.rep_pay = encounter_config.rep_pay;
to_update.rep_run = encounter_config.rep_run;
to_update.rep_fight = encounter_config.rep_fight;

to_update.min_rep = encounter_config.min_rep;
to_update.max_rep = encounter_config.max_rep;

to_update.payout = encounter_config.payout;

set!(self.world(), (to_update));
}

}

#[generate_trait]
impl InternalImpl of InternalTrait {
fn assert_caller_is_owner(self: @ContractState) {
// assert(self.world().is_owner(starknet::get_caller_address(), 0), 'only world owner');

assert(
self.world().is_owner(get_caller_address(), get_contract_address().into()),
'not owner'
);
}

}
}
327 changes: 327 additions & 0 deletions src/config/drugs.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,327 @@
use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait};

use rollyourown::{
traits::{Enumerable}, utils::introspect::{Bytes31IntrospectionImpl},
utils::{
bytes16::{Bytes16, Bytes16Impl, Bytes16Trait},
introspect::Bytes16IntrospectionImpl
}
};




#[derive(Copy, Drop, Serde, PartialEq, Introspect)]
enum Drugs {
Ludes,
Speed,
Weed,
Shrooms,
Acid,
Ketamine,
Heroin,
Cocaine,
}


#[derive(Model, Copy, Drop, Serde)]
struct DrugConfig {
#[key]
drug: Drugs,
drug_id: u8,
base: u16,
step: u16,
weight: u16,
name: Bytes16,
}


//
//
//

#[generate_trait]
impl DrugConfigImpl of DrugConfigTrait {
#[inline(always)]
fn get(world: IWorldDispatcher, drug: Drugs) -> DrugConfig {
get!(world, (drug), DrugConfig)
}
}

//
//
//

impl DrugsEnumerableImpl of Enumerable<Drugs> {
#[inline(always)]
fn all() -> Span<Drugs> {
let mut items = array![
Drugs::Ludes,
Drugs::Speed,
Drugs::Weed,
Drugs::Shrooms,
Drugs::Acid,
Drugs::Ketamine,
Drugs::Heroin,
Drugs::Cocaine
];
items.span()
}
}

//
//
//

impl DrugsIntoFelt252 of Into<Drugs, felt252> {
fn into(self: Drugs) -> felt252 {
match self {
Drugs::Ludes => 'Ludes',
Drugs::Speed => 'Speed',
Drugs::Weed => 'Weed',
Drugs::Shrooms => 'Shrooms',
Drugs::Acid => 'Acid',
Drugs::Ketamine => 'Ketamine',
Drugs::Heroin => 'Heroin',
Drugs::Cocaine => 'Cocaine',
}
}
}

impl DrugsIntoU8 of Into<Drugs, u8> {
fn into(self: Drugs) -> u8 {
match self {
Drugs::Ludes => 0,
Drugs::Speed => 1,
Drugs::Weed => 2,
Drugs::Shrooms => 3,
Drugs::Acid => 4,
Drugs::Ketamine => 5,
Drugs::Heroin => 6,
Drugs::Cocaine => 7,
}
}
}

impl U8IntoDrugs of Into<u8, Drugs> {
fn into(self: u8) -> Drugs {
let self252: felt252 = self.into();
match self252 {
0 => Drugs::Ludes,
1 => Drugs::Speed,
2 => Drugs::Weed,
3 => Drugs::Shrooms,
4 => Drugs::Acid,
5 => Drugs::Ketamine,
6 => Drugs::Heroin,
7 => Drugs::Cocaine,
_ => Drugs::Ludes,
}
}
}


//
//
//

fn initialize_drug_config(world: IWorldDispatcher) {
set!(
world,
DrugConfig {
drug: Drugs::Ludes,
drug_id: Drugs::Ludes.into(),
base: 24,
step: 2,
weight: 10,
name: Bytes16Impl::from('Ludes')
}
);

set!(
world,
DrugConfig {
drug: Drugs::Speed,
drug_id: Drugs::Speed.into(),
base: 150,
step: 8,
weight: 14,
name: Bytes16Impl::from('Speed')
}
);

set!(
world,
DrugConfig {
drug: Drugs::Weed,
drug_id: Drugs::Weed.into(),
base: 402,
step: 16,
weight: 19,
name: Bytes16Impl::from('Weed')
}
);

set!(
world,
DrugConfig {
drug: Drugs::Shrooms,
drug_id: Drugs::Shrooms.into(),
base: 906,
step: 32,
weight: 27,
name: Bytes16Impl::from('Shrooms')
}
);

set!(
world,
DrugConfig {
drug: Drugs::Acid,
drug_id: Drugs::Acid.into(),
base: 1914,
step: 64,
weight: 37,
name: Bytes16Impl::from('Acid')
}
);

set!(
world,
DrugConfig {
drug: Drugs::Ketamine,
drug_id: Drugs::Ketamine.into(),
base: 3930,
step: 128,
weight: 52,
name: Bytes16Impl::from('Ketamine')
}
);

set!(
world,
DrugConfig {
drug: Drugs::Heroin,
drug_id: Drugs::Heroin.into(),
base: 7962,
step: 256,
weight: 72,
name: Bytes16Impl::from('Heroin')
}
);

set!(
world,
DrugConfig {
drug: Drugs::Cocaine,
drug_id: Drugs::Cocaine.into(),
base: 16026,
step: 512,
weight: 100,
name: Bytes16Impl::from('Cocaine')
}
);
}


//
//
//

// fn initialize_drug_config_v0(world: IWorldDispatcher) {
// set!(
// world,
// DrugConfig {
// drug: Drugs::Ludes,
// drug_id: Drugs::Ludes.into(),
// base: 18,
// step: 1,
// weight: 5,
// name: Bytes16Impl::from('Ludes')
// }
// );

// set!(
// world,
// DrugConfig {
// drug: Drugs::Speed,
// drug_id: Drugs::Speed.into(),
// base: 85,
// step: 6,
// weight: 10,
// name: Bytes16Impl::from('Speed')
// }
// );

// set!(
// world,
// DrugConfig {
// drug: Drugs::Weed,
// drug_id: Drugs::Weed.into(),
// base: 290,
// step: 18,
// weight: 15,
// name: Bytes16Impl::from('Weed')
// }
// );

// set!(
// world,
// DrugConfig {
// drug: Drugs::Shrooms,
// drug_id: Drugs::Shrooms.into(),
// base: 980,
// step: 54,
// weight: 25,
// name: Bytes16Impl::from('Shrooms')
// }
// );

// set!(
// world,
// DrugConfig {
// drug: Drugs::Acid,
// drug_id: Drugs::Acid.into(),
// base: 2900,
// step: 111,
// weight: 30,
// name: Bytes16Impl::from('Acid')
// }
// );

// set!(
// world,
// DrugConfig {
// drug: Drugs::Ketamine,
// drug_id: Drugs::Ketamine.into(),
// base: 6800,
// step: 186,
// weight: 45,
// name: Bytes16Impl::from('Ketamine')
// }
// );

// set!(
// world,
// DrugConfig {
// drug: Drugs::Heroin,
// drug_id: Drugs::Heroin.into(),
// base: 13500,
// step: 231,
// weight: 65,
// name: Bytes16Impl::from('Heroin')
// }
// );

// set!(
// world,
// DrugConfig {
// drug: Drugs::Cocaine,
// drug_id: Drugs::Cocaine.into(),
// base: 19800,
// step: 284,
// weight: 100,
// name: Bytes16Impl::from('Cocaine')
// }
// );
// }
444 changes: 444 additions & 0 deletions src/config/encounters.cairo

Large diffs are not rendered by default.

63 changes: 63 additions & 0 deletions src/config/game.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait};

const GAME_CONFIG_KEY: u8 = 0;

#[derive(Model, Copy, Drop, Serde)]
struct GameConfig {
#[key]
key: u8,
cash: u32, // initial cash
health: u8, // initial health 0-100
//
max_turns: u8, // max game turn u6 : max 63
max_wanted_shopping: u8, // limit to enter pawnshop
max_rounds: u8, // max loop when running
//
encounter_count: u8, // total nb of encounters
//
rep_drug_step: u8, // reputation requiered to level up drug
rep_buy_item: u8, // reputation earn when buying item
rep_carry_drugs: u8, // reputation earn when traveling with >5 drug.quantity
rep_hospitalized: u8, // reputation earn when Hospitalized
rep_jailed: u8, // reputation earn when Jailed
}

#[generate_trait]
impl GameConfigImpl of GameConfigTrait {
#[inline(always)]
fn get(world: IWorldDispatcher) -> GameConfig {
get!(world, (GAME_CONFIG_KEY), GameConfig)
}

#[inline(always)]
fn set(world: IWorldDispatcher, game_config: GameConfig) {
let mut game_config = game_config;
game_config.key = GAME_CONFIG_KEY;

set!(world, (game_config));
}
}

fn initialize_game_config(world: IWorldDispatcher) {
set!(
world,
GameConfig {
key: GAME_CONFIG_KEY,
cash: 1000,
health: 100,
//
max_turns: 30,
max_wanted_shopping: 5,
max_rounds: 3,
//
encounter_count: 0,
//
rep_drug_step: 20,
rep_buy_item: 1,
rep_carry_drugs: 2,
rep_hospitalized: 3,
rep_jailed: 4,
}
);
}

535 changes: 535 additions & 0 deletions src/config/hustlers.cairo

Large diffs are not rendered by default.

178 changes: 178 additions & 0 deletions src/config/locations.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait};
use rollyourown::traits::{Enumerable, Randomizable};
use rollyourown::utils::random::{Random, RandomImpl};

use rollyourown::utils::bytes16::{Bytes16, Bytes16Impl, Bytes16Trait};
use rollyourown::utils::introspect::Bytes16IntrospectionImpl;


#[derive(Model, Copy, Drop, Serde)]
struct LocationConfig {
#[key]
location: Locations,
location_id: u8,
name: Bytes16,
}


#[derive(Copy, Drop, Serde, PartialEq, Introspect)]
enum Locations {
Home,
Queens,
Bronx,
Brooklyn,
Jersey,
Central,
Coney,
}

//
//
//

impl LocationsEnumerableImpl of Enumerable<Locations> {
fn all() -> Span<Locations> {
let mut items = array![
Locations::Queens,
Locations::Bronx,
Locations::Brooklyn,
Locations::Jersey,
Locations::Central,
Locations::Coney
];
items.span()
}

}

//
//
//

impl LocationsRandomizableImpl of Randomizable<Locations> {
fn random(ref randomizer: Random) -> Locations {
let locations = LocationsEnumerableImpl::all();
let index = randomizer.between::<u32>(0, locations.len().into());
*locations.at(index.try_into().unwrap())
}
}

//
//
//

impl LocationsIntoFelt252 of Into<Locations, felt252> {
fn into(self: Locations) -> felt252 {
match self {
Locations::Home => 'Home',
Locations::Queens => 'Queens',
Locations::Bronx => 'Bronx',
Locations::Brooklyn => 'Brooklyn',
Locations::Jersey => 'Jersey',
Locations::Central => 'Central',
Locations::Coney => 'Coney',
}
}
}

impl LocationsIntoU8 of Into<Locations, u8> {
fn into(self: Locations) -> u8 {
match self {
Locations::Home => 0,
Locations::Queens => 1,
Locations::Bronx => 2,
Locations::Brooklyn => 3,
Locations::Jersey => 4,
Locations::Central => 5,
Locations::Coney => 6,
}
}
}


impl U8IntoLocations of Into<u8, Locations> {
fn into(self: u8) -> Locations {
let self252: felt252 = self.into();
match self252 {
0 => Locations::Home,
1 => Locations::Queens,
2 => Locations::Bronx,
3 => Locations::Brooklyn,
4 => Locations::Jersey,
5 => Locations::Central,
6 => Locations::Coney,
_ => Locations::Home,
}
}
}


//
//
//

fn initialize_location_config(world: IWorldDispatcher) {
set!(
world,
LocationConfig {
location: Locations::Home,
location_id: Locations::Home.into(),
name: Bytes16Impl::from('Home')
}
);

set!(
world,
LocationConfig {
location: Locations::Queens,
location_id: Locations::Queens.into(),
name: Bytes16Impl::from('Queens')
}
);

set!(
world,
LocationConfig {
location: Locations::Bronx,
location_id: Locations::Bronx.into(),
name: Bytes16Impl::from('The Bronx')
}
);

set!(
world,
LocationConfig {
location: Locations::Brooklyn,
location_id: Locations::Brooklyn.into(),
name: Bytes16Impl::from('Brooklyn')
}
);

set!(
world,
LocationConfig {
location: Locations::Jersey,
location_id: Locations::Jersey.into(),
name: Bytes16Impl::from('Jersey City')
}
);

set!(
world,
LocationConfig {
location: Locations::Central,
location_id: Locations::Central.into(),
name: Bytes16Impl::from('Central Park')
}
);

set!(
world,
LocationConfig {
location: Locations::Coney,
location_id: Locations::Coney.into(),
name: Bytes16Impl::from('Coney Island')
}
);
}

40 changes: 40 additions & 0 deletions src/config/ryo.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use starknet::ContractAddress;
use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait};

const RYO_CONFIG_KEY: u8 = 0;

#[derive(Model, Copy, Drop, Serde)]
struct RyoConfig {
#[key]
key: u8,
initialized: bool,
paused: bool,
//
leaderboard_version: u16,
leaderboard_duration: u32,
//
paper_fee: u16,
treasury_fee_pct: u8,
treasury_balance: u32,
}

#[derive(Copy, Drop)]
struct RyoConfigManager {
world: IWorldDispatcher
}

#[generate_trait]
impl RyoConfigImpl of RyoConfigManagerTrait {
fn new(world: IWorldDispatcher) -> RyoConfigManager {
RyoConfigManager { world }
}

fn get(self: RyoConfigManager) -> RyoConfig {
get!(self.world, (RYO_CONFIG_KEY), RyoConfig)
}

fn set(self: RyoConfigManager, ryo_config: RyoConfig) {
set!(self.world, (ryo_config));
}

}
43 changes: 43 additions & 0 deletions src/config/ryo_address.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use starknet::ContractAddress;
use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait};

const RYO_ADDRESS_CONFIG_KEY: u8 = 0;

#[derive(Model, Copy, Drop, Serde)]
struct RyoAddress {
#[key]
key: u8,
paper: ContractAddress,
treasury: ContractAddress,
}

#[derive(Copy, Drop)]
struct RyoAddressManager {
world: IWorldDispatcher
}

#[generate_trait]
impl RyoAddressImpl of RyoAddressManagerTrait {
fn new(world: IWorldDispatcher) -> RyoAddressManager {
RyoAddressManager { world }
}

fn get(self: RyoAddressManager) -> RyoAddress {
get!(self.world, (RYO_ADDRESS_CONFIG_KEY), RyoAddress)
}

fn set(self: RyoAddressManager, ryo_address: RyoAddress) {
set!(self.world, (ryo_address));
}

// getters

fn paper(self: RyoAddressManager) -> ContractAddress {
self.get().paper
}

fn treasury(self: RyoAddressManager) -> ContractAddress {
self.get().treasury
}

}
2 changes: 1 addition & 1 deletion src/constants.cairo
Original file line number Diff line number Diff line change
@@ -1 +1 @@
const SCALING_FACTOR: u128 = 10_000;
const ETHER: u256 = 1_000_000_000_000_000_000;
9 changes: 9 additions & 0 deletions src/interfaces/paper.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use starknet::ContractAddress;

#[starknet::interface]
trait IPaper<TState> {
fn transfer(ref self: TState, recipient: ContractAddress, amount: u256) -> bool;
fn transfer_from(
ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256
) -> bool;
}
66 changes: 63 additions & 3 deletions src/lib.cairo
Original file line number Diff line number Diff line change
@@ -1,7 +1,67 @@
mod models;
mod systems;
mod config {
mod config;
mod drugs;
mod encounters;
mod game;
mod hustlers;
mod locations;
mod ryo;
mod ryo_address;
}

mod models {
mod game;
mod game_store_packed;

mod leaderboard;
}

mod packing {
mod game_store;
mod game_store_layout;

mod drugs_packed;
mod markets_packed;
mod items_packed;
mod wanted_packed;

mod player;
mod player_layout;
}

mod systems {
mod ryo;
mod leaderboard;

mod game;
mod game_loop;

mod trading;
mod shopping;
mod traveling;

mod devtools;
}

mod utils {
mod bytes16;
mod random;
mod math;
mod events;
mod bits;
mod introspect;
}

mod interfaces {
mod paper;
}

mod _mocks {
mod paper_mock;
}

mod traits;
mod constants;
mod utils;

#[cfg(test)]
mod tests;
9 changes: 0 additions & 9 deletions src/models.cairo

This file was deleted.

100 changes: 0 additions & 100 deletions src/models/drug.cairo

This file was deleted.

141 changes: 0 additions & 141 deletions src/models/encounter.cairo

This file was deleted.

73 changes: 22 additions & 51 deletions src/models/game.cairo
Original file line number Diff line number Diff line change
@@ -1,67 +1,38 @@
use starknet::ContractAddress;
use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait};

use rollyourown::utils::bytes16::Bytes16;
use rollyourown::utils::introspect::Bytes16IntrospectionImpl;

#[derive(Model, Copy, Drop, Serde)]
struct Game {
#[key]
game_id: u32,
#[key]
player_id: ContractAddress, // check if can remove key
//
player_name: Bytes16,
hustler_id: u16,
//
leaderboard_version: u16,
game_mode: GameMode,
start_time: u64,
max_players: usize,
num_players: usize,
max_turns: usize,
creator: ContractAddress,
//
max_turns: u8,
max_wanted_shopping: u8,
max_rounds: u8,
//
game_over: bool,
}

#[derive(Copy, Drop, Serde, PartialEq)]
#[derive(Copy, Drop, Serde, PartialEq, Introspect)]
enum GameMode {
Test,
Unlimited
Dealer,
Warrior,
}


#[generate_trait]
impl GameImpl of GameTrait {
#[inline(always)]
fn tick(self: Game) -> bool {
let info = starknet::get_block_info().unbox();

if info.block_timestamp < self.start_time {
return false;
}

true
fn get(world: IWorldDispatcher, game_id: u32, player_id: ContractAddress) -> Game {
get!(world, (game_id, player_id), Game)
}
}


use dojo::database::introspect::{
Enum, Member, Ty, Struct, Introspect, serialize_member, serialize_member_type
};

impl GameModeIntrospectionImpl of Introspect<GameMode> {
#[inline(always)]
fn size() -> usize {
1
}

#[inline(always)]
fn layout(ref layout: Array<u8>) {
layout.append(8);
}

#[inline(always)]
fn ty() -> Ty {
Ty::Enum(
Enum {
name: 'GameMode',
attrs: array![].span(),
children: array![
('Limited', serialize_member_type(@Ty::Tuple(array![].span()))),
('Unlimited', serialize_member_type(@Ty::Tuple(array![].span()))),
]
.span()
}
)
}
}

11 changes: 11 additions & 0 deletions src/models/game_store_packed.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use starknet::ContractAddress;

#[derive(Model, Copy, Drop, Serde)]
struct GameStorePacked {
#[key]
game_id: u32,
#[key]
player_id: ContractAddress,
packed: felt252
}

90 changes: 0 additions & 90 deletions src/models/item.cairo

This file was deleted.

13 changes: 10 additions & 3 deletions src/models/leaderboard.cairo
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
use starknet::ContractAddress;

// TODO: should be renamed Season
#[derive(Model, Copy, Drop, Serde)]
struct Leaderboard {
#[key]
version: u32,
high_score: u128,
version: u16,
game_id: u32,
player_id: ContractAddress,
high_score: u32,
//
next_version_timestamp: u64,
//
paper_balance: u32,
claimed: bool,
}

108 changes: 0 additions & 108 deletions src/models/location.cairo

This file was deleted.

74 changes: 0 additions & 74 deletions src/models/market.cairo

This file was deleted.

154 changes: 0 additions & 154 deletions src/models/player.cairo

This file was deleted.

33 changes: 0 additions & 33 deletions src/models/ryo.cairo

This file was deleted.

50 changes: 50 additions & 0 deletions src/packing/drugs_packed.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use starknet::ContractAddress;
use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait};

use rollyourown::{
config::{drugs::{Drugs}},
utils::bits::{Bits, BitsImpl, BitsDefaultImpl, BitsTrait, BitsMathImpl},
models::game::{Game, GameMode, GameImpl},
};

// 16 bits : 3 bits for Drugs, 13 bits for quantity
#[derive(Copy, Drop)]
struct DrugsPacked {
world: IWorldDispatcher,
game: Game,
//
packed: felt252
}

#[derive(Copy, Drop)]
struct DrugsUnpacked {
drug: Drugs,
quantity: u32,
}


#[generate_trait]
impl DrugsPackedImpl of DrugsPackedTrait {
fn new(world: IWorldDispatcher, game: Game) -> DrugsPacked {
DrugsPacked { world, game, packed: 0 }
}

fn get(self: @DrugsPacked) -> DrugsUnpacked {
let mut bits = BitsImpl::from_felt(*self.packed);

let drug: Drugs = bits.extract_into::<u8>(0, 3).into();
let quantity: u32 = bits.extract_into::<u32>(3, 13).into();

DrugsUnpacked { drug, quantity }
}

fn set(ref self: DrugsPacked, drugs_unpacked: DrugsUnpacked) {
let mut bits = BitsDefaultImpl::default();

bits.replace::<u8>(0, 3, drugs_unpacked.drug.into());
bits.replace::<u32>(3, 13, drugs_unpacked.quantity.into());

self.packed = bits.into_felt();
}
}

145 changes: 145 additions & 0 deletions src/packing/game_store.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
use starknet::ContractAddress;
use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait};

use rollyourown::{
models::{game_store_packed::{GameStorePacked}, game::{Game, GameMode, GameImpl}},
packing::{
game_store_layout::{
GameStoreLayout, GameStoreLayoutEnumerableImpl, GameStoreLayoutPackableImpl
},
drugs_packed::{DrugsPacked, DrugsPackedImpl},
wanted_packed::{WantedPacked, WantedPackedImpl},
markets_packed::{MarketsPacked, MarketsPackedImpl},
items_packed::{ItemsPacked, ItemsPackedImpl},
player::{Player, PlayerImpl, PlayerPackerImpl, PlayerUnpackerImpl},
},
utils::bits::{Bits, BitsImpl, BitsTrait, BitsDefaultImpl}, traits::{Packable, Packer, Unpacker}
};


#[derive(Copy, Drop)]
struct GameStore {
world: IWorldDispatcher,
game: Game,
//
markets: MarketsPacked,
items: ItemsPacked,
drugs: DrugsPacked,
wanted: WantedPacked,
player: Player,
}

// init new game state
#[generate_trait]
impl GameStoreImpl of GameStoreTrait {
fn new(world: IWorldDispatcher, game: Game) -> GameStore {
GameStore {
world,
game,
//
markets: MarketsPackedImpl::new(world, game),
items: ItemsPackedImpl::new(world, game),
drugs: DrugsPackedImpl::new(world, game),
wanted: WantedPackedImpl::new(world, game),
player: PlayerImpl::new(world, game),
}
}

fn get(world: IWorldDispatcher, game: Game) -> GameStore {
let game_store_packed = get!(world, (game.game_id, game.player_id), GameStorePacked);
game_store_packed.unpack(world, game)
}
}


// pack
impl GameStorePackerImpl of Packer<GameStore, GameStorePacked> {
fn pack(self: GameStore) -> GameStorePacked {
let mut bits = BitsDefaultImpl::default();
let mut layout = GameStoreLayoutEnumerableImpl::all();

loop {
match layout.pop_front() {
Option::Some(item) => {
match *item {
GameStoreLayout::Markets => {
bits.replace::<felt252>(item.idx(), item.bits(), self.markets.packed);
},
GameStoreLayout::Items => {
bits.replace::<felt252>(item.idx(), item.bits(), self.items.packed);
},
GameStoreLayout::Drugs => {
bits.replace::<felt252>(item.idx(), item.bits(), self.drugs.packed);
},
GameStoreLayout::Wanted => {
bits.replace::<felt252>(item.idx(), item.bits(), self.wanted.packed);
},
GameStoreLayout::Player => {
let player_packed: felt252 = self.player.pack();
bits.replace::<felt252>(item.idx(), item.bits(), player_packed);
},
};
},
Option::None => { break; },
};
};

GameStorePacked {
game_id: self.game.game_id, player_id: self.game.player_id, packed: bits.into_felt(),
}
}
}

// unpack
impl GameStoreUnpackerImpl of Unpacker<GameStorePacked, GameStore> {
fn unpack(self: GameStorePacked, world: IWorldDispatcher, game: Game,) -> GameStore {
let mut game_store = GameStoreImpl::new(world, game);
let mut layout = GameStoreLayoutEnumerableImpl::all();
let bits = BitsImpl::from_felt(self.packed);

loop {
match layout.pop_front() {
Option::Some(item) => {
let packed = bits.extract_into::<felt252>(item.idx(), item.bits());

match *item {
GameStoreLayout::Markets => {
game_store.markets = MarketsPacked { world, game, packed };
},
GameStoreLayout::Items => {
game_store.items = ItemsPacked { world, game, packed };
},
GameStoreLayout::Drugs => {
game_store.drugs = DrugsPacked { world, game, packed };
},
GameStoreLayout::Wanted => {
game_store.wanted = WantedPacked { world, game, packed };
},
GameStoreLayout::Player => {
// unpack packed into Player
game_store.player = packed.unpack(world, game);
},
};
},
Option::None => { break; },
};
};

game_store
}
}


#[cfg(test)]
mod tests {
use rollyourown::models::game_store_packed::GameStorePacked;
use super::{GameStorePackerImpl};
// #[test]
// #[available_gas(100000000)]
// fn test_game_store_pack() {
// let mut game_store = GameStoreDefaultImpl::default();
// let game_store_packed: GameStorePacked = game_store.pack();
// assert(game_store_packed.packed == 0, 'should be 0');
// }
}

90 changes: 90 additions & 0 deletions src/packing/game_store_layout.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
use rollyourown::{traits::{Enumerable, Packable},};

#[derive(Copy, Drop, Serde, PartialEq)]
enum GameStoreLayout {
Markets,
Items,
Drugs,
Wanted,
Player,
}

impl GameStoreLayoutIntoBytes31Impl of Into<GameStoreLayout, bytes31> {
fn into(self: GameStoreLayout) -> bytes31 {
let value = match self {
GameStoreLayout::Markets => 'Markets',
GameStoreLayout::Items => 'Items',
GameStoreLayout::Drugs => 'Drugs',
GameStoreLayout::Wanted => 'Wanted',
GameStoreLayout::Player => 'Player',
};
value.try_into().unwrap()
}
}


// Enumerable

impl GameStoreLayoutEnumerableImpl of Enumerable<GameStoreLayout> {
fn all() -> Span<GameStoreLayout> {
let items = array![
GameStoreLayout::Markets,
GameStoreLayout::Items,
GameStoreLayout::Drugs,
GameStoreLayout::Wanted,
GameStoreLayout::Player,
];
items.span()
}
}

// Packable

impl GameStoreLayoutPackableImpl of Packable<GameStoreLayout> {
fn bits(self: @GameStoreLayout) -> u8 {
match *self {
GameStoreLayout::Markets => 144,
GameStoreLayout::Items => 8,
GameStoreLayout::Drugs => 16,
GameStoreLayout::Wanted => 18,
GameStoreLayout::Player => 64,
}
}

fn idx(self: @GameStoreLayout) -> u8 {
let mut layout = GameStoreLayoutEnumerableImpl::all();
let mut idx = 0_u8;

loop {
match layout.pop_front() {
Option::Some(i) => { if self == i {
break;
} else {
idx += i.bits()
} },
Option::None => { break; }
}
};
idx
}
}


#[cfg(test)]
mod tests {
use super::{GameStoreLayout, GameStoreLayoutPackableImpl};

#[test]
#[available_gas(100000000)]
fn test_layout_idx() {
assert(GameStoreLayout::Markets.idx() == 0, 'bad markets idx');
assert(GameStoreLayout::Items.idx() == 144, 'bad items idx');
}

#[test]
#[available_gas(100000000)]
fn test_layout_size() {
assert(GameStoreLayout::Markets.bits() == 144, 'bad markets size');
assert(GameStoreLayout::Items.bits() == 8, 'bad items size');
}
}
102 changes: 102 additions & 0 deletions src/packing/items_packed.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use starknet::ContractAddress;
use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait};
use rollyourown::{
config::hustlers::{HustlerItemConfig, HustlerImpl, ItemSlot},
models::game::{Game, GameMode},
utils::bits::{Bits, BitsImpl, BitsTrait, BitsMathImpl},
packing::game_store::{GameStore}
};


#[derive(Copy, Drop)]
struct ItemsPacked {
world: IWorldDispatcher,
game: Game,
//
packed: felt252
}


#[generate_trait]
impl ItemsPackedImpl of ItemsPackedTrait {
fn new(world: IWorldDispatcher, game: Game) -> ItemsPacked {
ItemsPacked { world, game, packed: 0 }
}

#[inline(always)]
fn get_slot_size(self: ItemsPacked) -> u8 {
2
}

fn get_item(self: ItemsPacked, slot: ItemSlot) -> HustlerItemConfig {
let bits = BitsImpl::from_felt(self.packed);

let size: u8 = self.get_slot_size();
let index: u8 = slot.into() * size;
let level: u8 = bits.extract_into::<u8>(index, size).into();

let hustler = HustlerImpl::get(self.world, self.game.hustler_id);
hustler.get_item_config(slot, level)
}

// assume you checked its possible or overflow crack boom OD
fn upgrade_item(ref self: ItemsPacked, slot: ItemSlot) {
let mut bits = BitsImpl::from_felt(self.packed);

let size: u8 = self.get_slot_size();
let index: u8 = slot.into() * size;
let level = bits.extract_into::<u8>(index, size);

bits.replace::<u8>(index, size, level + 1);

self.packed = bits.into_felt();
}


//
//
//

#[inline(always)]
fn attack_item(self: ItemsPacked) -> HustlerItemConfig {
self.get_item(ItemSlot::Weapon)
}

#[inline(always)]
fn defense_item(self: ItemsPacked) -> HustlerItemConfig {
self.get_item(ItemSlot::Clothes)
}

#[inline(always)]
fn speed_item(self: ItemsPacked) -> HustlerItemConfig {
self.get_item(ItemSlot::Feet)
}

#[inline(always)]
fn transport_item(self: ItemsPacked) -> HustlerItemConfig {
self.get_item(ItemSlot::Transport)
}

// stats

#[inline(always)]
fn attack(self: ItemsPacked) -> u8 {
self.attack_item().tier.stat.try_into().unwrap()
}

#[inline(always)]
fn defense(self: ItemsPacked) -> u8 {
self.defense_item().tier.stat.try_into().unwrap()
}

#[inline(always)]
fn speed(self: ItemsPacked) -> u8 {
self.speed_item().tier.stat.try_into().unwrap()
}

#[inline(always)]
fn transport(self: ItemsPacked) -> u32 {
self.transport_item().tier.stat
}
}

203 changes: 203 additions & 0 deletions src/packing/markets_packed.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
use core::traits::TryInto;
use starknet::ContractAddress;
use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait};

use rollyourown::{
models::game::{Game, GameMode}, systems::game::game::HighVolatility,
utils::{
events::{RawEventEmitterTrait, RawEventEmitterImpl},
random::{Random, RandomImpl, RandomTrait}, math::{MathTrait, MathImplU8},
bits::{Bits, BitsImpl, BitsTrait, BitsMathImpl},
},
config::{
drugs::{Drugs, DrugsEnumerableImpl, DrugConfig, DrugConfigImpl},
locations::{Locations, LocationsEnumerableImpl},
},
packing::game_store_layout::{GameStoreLayout, GameStoreLayoutPackableImpl},
};


#[derive(Copy, Drop)]
struct MarketsPacked {
world: IWorldDispatcher,
game: Game,
//
packed: felt252
}

#[generate_trait]
impl MarketsPackedImpl of MarketsPackedTrait {
#[inline(always)]
fn new(world: IWorldDispatcher, game: Game) -> MarketsPacked {
let packed: u256 = core::pedersen::pedersen(game.game_id.into(), game.player_id.into())
.into();
let mask = BitsMathImpl::mask::<u256>(GameStoreLayout::Markets.bits());
let safe_packed: felt252 = (packed & mask).try_into().unwrap();
MarketsPacked { world, game, packed: safe_packed }
}

#[inline(always)]
fn get_drugs_by_location(self: MarketsPacked) -> u8 {
4
}

#[inline(always)]
fn get_slot_size(self: MarketsPacked) -> u8 {
6
}

#[inline(always)]
fn get_drug_config(ref self: MarketsPacked, drug: Drugs) -> DrugConfig {
get!(self.world, (drug), DrugConfig)
}

fn get_tick(ref self: MarketsPacked, location: Locations, drug: Drugs) -> usize {
let bits = BitsImpl::from_felt(self.packed);

let location_idx: u8 = location.into() - 1;
let drug_idx: u8 = drug.into() % self.get_drugs_by_location();

let size: u8 = self.get_slot_size();
let start: u8 = (location_idx * self.get_drugs_by_location() + drug_idx) * size;

bits.extract_into::<usize>(start, size)
}


fn get_drug_price(ref self: MarketsPacked, location: Locations, drug: Drugs) -> usize {
let drug_config = self.get_drug_config(drug);
let tick = self.get_tick(location, drug);

tick * drug_config.step.into() + drug_config.base.into()
}

//
//
//

fn set_tick(ref self: MarketsPacked, location: Locations, drug: Drugs, value: usize) {
let mut bits = BitsImpl::from_felt(self.packed);

let location_idx: u8 = location.into() - 1;
let drug_idx: u8 = drug.into() % self.get_drugs_by_location();

let size: u8 = self.get_slot_size();
let start: u8 = (location_idx * self.get_drugs_by_location() + drug_idx) * size;

bits.replace::<usize>(start, size, value);
self.packed = bits.into_felt();
}

//
//
//

fn market_variations(ref self: MarketsPacked, ref randomizer: Random) {
let mut locations = LocationsEnumerableImpl::all();

loop {
match locations.pop_front() {
Option::Some(location) => {

// limit to 4 drugs slots == [0,1,2,3]
let mut drugs = array![
Drugs::Ludes,
Drugs::Speed,
Drugs::Weed,
Drugs::Shrooms,
].span();

loop {
match drugs.pop_front() {
Option::Some(drug) => {
let rand: u16 = randomizer.between::<u16>(1, 1000).into();
let tick = self.get_tick(*location, *drug);
let direction = rand > 500;

// kind of dopessian distribution
let variation = if rand <= 200 || rand >= 799 {
1_u32
} else if rand <= 350 || rand >= 649 {
2_u32
} else if rand <= 475 || rand >= 524 {
4_u32
} else if rand <= 495 || rand >= 504 {
6_u32
} else {
12_u32
};

let new_tick = if direction {
tick.add_capped(variation, 63)
} else {
tick.sub_capped(variation, 0)
};

self.set_tick(*location, *drug, new_tick);

if variation == 12_u32 {
// emit HighVolatility
self
.world
.emit_raw(
array![
selector!("HighVolatility"),
Into::<u32, felt252>::into(self.game.game_id),
Into::<
ContractAddress, felt252
>::into(self.game.player_id),
],
array![
Into::<Locations, u8>::into(*location).into(),
Into::<Drugs, u8>::into(*drug).into(),
Into::<bool, felt252>::into(direction),
],
);
}
},
Option::None => { break; }
};
};
},
Option::None(_) => { break; }
};
};
}

//
//
//

fn shuffle_drug_prices(ref self: MarketsPacked, ref randomizer: Random, drug_slot: u8) {
let mut locations = LocationsEnumerableImpl::all();

loop {
match locations.pop_front() {
Option::Some(location) => {
let rand_tick = randomizer.between::<usize>(0, 63).into();
self.set_tick(*location, drug_slot.into(), rand_tick);
},
Option::None(_) => { break; }
};
};
}
}

//
//
//

#[cfg(test)]
mod tests {
use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait};
use super::{MarketsPacked, MarketsPackedImpl, RandomImpl};
// #[test]
// #[available_gas(100000000)]
// fn test_markets_variations_v1() {
// let world = dojo::test_utils::spawn_test_world(array![]);

// let mut market_packed = MarketsPackedImpl::new(world, 0, 0.try_into().unwrap());
// let mut randomizer = RandomImpl::new(world);
// market_packed.market_variations(ref randomizer);
// }
}
Loading