Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,7 @@ node_modules/

.deploys/

AGENTS.md
AGENTS.md

bun.lock
package.json
54 changes: 52 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,11 @@ chomp/
│ │ ├── StandardAttackStructs.sol # ATTACK_PARAMS struct
│ │ └── AttackCalculator.sol # Damage calculation
│ ├── rng/ # Randomness oracle interface
│ ├── teams/ # Team registry (combined team + mon registry + gacha)
│ ├── teams/ # Team registry (combined team + mon registry + gacha + progression)
│ │ ├── ITeamRegistry.sol
│ │ └── GachaTeamRegistry.sol
│ │ ├── GachaTeamRegistry.sol # Roll, exp, daily multipliers, onBattleEnd hook
│ │ ├── Facets.sol # 12-facet ±5% stat tradeoff system (abstract)
│ │ └── Quests.sol # Daily quest pool + packed predicate evaluator (abstract)
│ └── types/ # Type effectiveness calculator
├── test/ # Foundry test suite
│ ├── abstract/BattleHelper.sol # Shared test helper (battle setup, commit-reveal)
Expand Down Expand Up @@ -196,6 +198,54 @@ Effects can be per-mon (local) or global (battlefield-wide). The `StaminaRegen`

16 types: Yin, Yang, Earth, Liquid, Fire, Metal, Ice, Nature, Lightning, Mythic, Air, Math, Cyber, Wild, Cosmic, None. Type effectiveness is calculated by `ITypeCalculator`.

### Gacha & Progression System

`GachaTeamRegistry` is also an `IEngineHook` (subscribes to `OnBattleEnd`) and inherits `Facets` + `Quests`. It owns the full progression loop: rolling, points, per-mon exp, daily multipliers, level-up facet draws, and quest evaluation.

**Rolling.** Mon ids are sequential starting at 0 (`createMon` enforces `monId == monIds.length()`). Ids `[0, NUM_STARTERS)` (= 3) are *starter* mons.

- `firstRoll(uint256 starterId)` — one-shot per player. Caller picks `starterId ∈ {0,1,2}`; the contract guarantees that mon at slot 0 of the result and rolls `INITIAL_ROLLS - 1` (= 3) more uniformly from `[NUM_STARTERS, numMons)`. Free.
- `roll(uint256 numRolls)` — paid (`ROLL_COST` per roll, default 7 points). Uniform across the entire pool. Reverts `NoMoreStock` once the caller owns every mon.
- Linear-probing dedup keeps draws inside their window so `firstRoll`'s 3 random picks never land on a starter.

**Points / exp / facets storage.** All packed for gas:

```
playerData[address] (1 slot per player):
bit 255 bonusAwarded (first-roll bonus claimed)
bit 254 isWhitelistedAsOpponent (admin-set; replaces a separate mapping)
bits 192-223 lastQuestCompletedDay (uint32, day = block.timestamp / 1 days)
bits 160-191 lastPvPDay (uint32)
bits 128-159 lastGameDay (uint32)
bits 0-127 pointsBalance (uint128)

packedExpForMon[player][monId / 16]: 16 mons × 16 bits each, capped at 65535.
facetData[player][monId / 16]: 16 mons × 16 bits each
(bits 0-11 unlockedBitmap, bits 12-15 assignedFacetId).
```

Both per-mon mappings share the same 16-mon bucketing so `_applyExpAndFacetDraws` walks the team in one pass and coalesces SSTOREs by bucket.

**Battle rewards (`onBattleEnd`).** CPU side is short-circuited (no SSTOREs, no event). For each human side:

- Base points: `POINTS_PER_WIN` (3) on win, `POINTS_PER_LOSS` (2) otherwise.
- First-roll bonus: `+ROLL_COST` on the player's first ever battle (one-shot).
- Per-mon exp: `EXP_PER_SURVIVING_MON` (2) for alive slots, `EXP_PER_KOD_MON` (1) for KO'd slots.
- Multipliers stack multiplicatively: `EXP_FIRST_GAME_OF_DAY_MULT` (×2) on the player's first battle of any kind that day, `EXP_FIRST_PVP_OF_DAY_MULT` (×2) on the first PvP battle that day, `QUEST_REWARD_EXP_MULT` (×2) when the active quest completes. Max stack = ×8.
- Quest reward: winner-only, one-shot per day; adds `QUEST_REWARD_POINTS` (2) on top.
- Level-ups (12-tier curve, capped at level 12 to match `TOTAL_FACETS`) trigger one facet draw per level crossed.

**Facets.** 12 systematically-derived ±5% stat tradeoffs across 4 stat groups (`HP`, `Atk`, `Def`, `Speed`). `_facetDef(facetId)` is pure — no constant table. Unlocks are persistent per-mon; `assignFacets(monIds, facetIds)` is a free bulk re-assign that requires the caller to own every listed mon and the facet to be in the unlocked bitmap (`facetId == 0` clears). Active facets shift base stats at battle start (Engine applies deltas after validator, so the validator still sees base stats).

**CPU opponent facets.** When fighting a whitelisted opponent (CPU), the human caller picks the CPU's team *and* its facet config in one call: `setOpponentTeam(opponent, monIndices, facetIds)`. Per-user-per-CPU storage (`opponentTeamFacetsPacked[opponent][phantomKey]`) keyed by the same `uint16(uint160(msg.sender))` phantom slot as the team. No ownership/unlock checks — any facet 0..12 is allowed. `getTeamsWithDeltas` short-circuits to this slot-indexed config when a side is `isWhitelistedOpponent`, so per-user CPU facet configurations stay isolated even when many users fight the same CPU.

**Quests.** Owner-managed `questPool` + a single `activeQuestPacked` slot (current day + active quest id). One quest is active per day, picked pseudorandomly via lazy rotation at the *end* of `onBattleEnd` so the current battle is judged against the pre-rotation quest. Each quest has up to `MAX_PREDICATES_PER_QUEST` (6) AND-composed predicates packed into one storage slot (41 bits each: `op` 5b, `cmp` 3b, `negate` 1b, `arg` 16b, `operand` 16b — total 246b + 3b count). Opcodes cover battle context (`TURNS`, `ALIVE_COUNT`, `ACTIVE_SLOT_INDEX`, `MON_KO_AT_SLOT`), team composition (`HAS_MON_ID`), per-mon progression (`MON_LEVEL`, `MON_FACET`), and live battle state (`MON_STATE` via `Engine.getMonStateForBattle`).

**Events.**

- `Roll(address indexed player, uint256[] monIds, uint256 pointsSpent)` — fires on both `firstRoll` (spend = 0) and paid `roll`.
- `GachaEvent(address indexed player, uint256 packed)` — one per non-CPU player per battle. Layout sized for `MONS_PER_TEAM` up to 8: points (bits 0-15), per-mon exp gain (bits 16-79, 8 lanes × 8b), per-mon facets unlocked this battle (bits 80-111, 8 lanes × 4b), `BONUS_*` flags (bits 112-119: `FIRST_ROLL` | `FIRST_GAME` | `FIRST_PVP` | `QUEST`), multiplier (bits 120-127), outcome (bits 128-135: 0=loss, 1=win, 2=draw). Lanes saturate so a future tuning blow-up can't bleed into neighbouring fields.

### Storage Architecture

- `BattleData` and `BattleConfig` are stored per battle key (derived from player addresses)
Expand Down
1 change: 1 addition & 0 deletions drool/abilities.csv
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ Interweaving,Inutia,"When Inutia swaps in, the opposing mon's ATK decreases 10%.
Up Only,Aurox,"Whenever Aurox takes damage, they gain a persistent 10% ATK boost."
Dreamcatcher,Xmon,"Whenever Xmon gains stamina, heal 6.6% of max HP."
Savior Complex,Ekineki,"On switch-in, gain a temporary 15/25/30% SpATK boost based on KO'd mons (1/2/3+). Triggers once per game."
Adaptor,Nirvamma,"When Nirvamma takes damage for the first time each game, they adapt to that source of damage. Nirvamma take 50% less damage from that move or effect."
Binary file modified drool/imgs/Nirvamma_Front.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added drool/imgs/aurox_front_damage_4x.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added drool/imgs/ekineki_front_damage_4x.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added drool/imgs/embursa_front_damage_4x.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added drool/imgs/ghouliath_front_damage_4x.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added drool/imgs/gorillax_front_damage_4x.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added drool/imgs/iblivion_front_damage_4x.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified drool/imgs/inutia_front_damage.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added drool/imgs/inutia_front_damage_4x.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added drool/imgs/malalien_front_damage_4x.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added drool/imgs/nirvamma_front_damage.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
Binary file added drool/imgs/pengym_front_damage_4x.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions drool/imgs/scale_gifs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/bash

for f in *_damage.gif; do
[ -f "$f" ] || continue
filename="${f%.gif}"
magick "$f" \
-filter point \
-interpolate nearest \
-resize 400% \
"${filename}_4x.gif"
echo "Scaled: $f → ${filename}_4x.gif"
done
Binary file added drool/imgs/sofabbi_front_damage_4x.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added drool/imgs/volthare_front_damage_4x.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added drool/imgs/xmon_front_damage_4x.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions drool/mons.csv
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ Id,Name,HP,Attack,Defense,SpecialAttack,SpecialDefense,Speed,Type1,Type2,Flavor
9,Aurox,400,150,230,100,220,100,Metal,NA,"Its gold sheen never fades away, serving as a symbol of optimism."
10,Xmon,311,123,179,222,185,285,Cosmic,NA,"bGJoIHBuYSBmcnIgdmcgdmEgbGJoZSBxZXJuemY="
11,Ekineki,299,130,180,280,175,266,Liquid,NA,"Born from a single drop of water."
12,Nirvamma,373,202,168,140,202,177,Math,NA,"Supposedly watches the entire universe."
8 changes: 6 additions & 2 deletions drool/moves.csv
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,13 @@ Iron Wall,Aurox,0,3,100,0,Metal,Self,"Until Aurox switches out, regenerate 50% o
Bull Rush,Aurox,120,2,100,0,Metal,Physical,Deals damage. Also deals 20% of max HP to Aurox.,,none
Contagious Slumber,Xmon,0,2,100,0,Cosmic,Other,"Inflicts Sleep on self and opponent. When asleep, you are forced to rest.",,none
Vital Siphon,Xmon,40,2,90,0,Cosmic,Special,"Deals damage, 50% chance to steal 1 stamina from opponent.",,none
Somniphobia,Xmon,0,1,100,0,Cosmic,Other,"For the next 6 turns, any mon that rests will take 1/8th of max HP as damage.",,none
Somniphobia,Xmon,0,1,100,0,Cosmic,Other,"For the next 8 turns, any mon that rests will take 1/8th of max HP as damage.",,none
Night Terrors,Xmon,0,0,100,0,Cosmic,Special,Gain a Terror stack. Deals damage and costs stamina at end of turn for each Terror stack. Deals extra damage if opponent is alseep.,,none
Bubble Bop,Ekineki,50,3,100,0,Liquid,Special,Hits twice. Each hit deals 50 base power.,,none
Sneak Attack,Ekineki,60,2,100,0,Liquid,Special,Hits any opponent mon (even non-active). Can only be used once per switch-in.,,opponent-mon
Nine Nine Nine,Ekineki,0,1,100,0,Math,Self,Sets crit rate to 90% on the next turn for all moves.,,none
Overflow,Ekineki,90,3,100,0,Math,Special,Deals damage.,,none
Overflow,Ekineki,90,3,100,0,Math,Special,Deals damage.,,none
Hard Reset,Nirvamma,0,2,100,0,Math,Other,"The next time the your team rests, the resting mon gains +1 stamina, heals 1/16 max HP, and is swapped out. The next time your opponent rests, their mon loses 1 stamina, takes 1/16 max HP damage, and also swaps out.",,none
Scary Numbers,Nirvamma,80,3,100,0,Math,Physical,Deals damage with a 20% chance to inflict Panic.,,none
Chronoffense,Nirvamma,?,2,100,0,Math,Physical,"Deals damage equal to how much time has passed since the move was last used.",,none
Modal Bolt,Nirvamma,90,3,100,0,Math,Physical,"Choose between Fire, Ice, or Lightning each use. Each mode is usable once, and applies its corresponding status (Burn / Frostbite / Zap) at 20% chance.",,none
2 changes: 1 addition & 1 deletion processing/generateSolidity.py
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,7 @@ def generate_solidity_script(mons: Dict[str, MonData], contracts: Dict[str, Cont
"pragma solidity ^0.8.0;",
"",
"import {Script} from \"forge-std/Script.sol\";",
"import {GachaTeamRegistry} from \"../src/teams/GachaTeamRegistry.sol\";",
"import {GachaTeamRegistry} from \"../src/game-layer/GachaTeamRegistry.sol\";",
"import {MonStats} from \"../src/Structs.sol\";",
"import {Type} from \"../src/Enums.sol\";",
""
Expand Down
2 changes: 1 addition & 1 deletion processing/generate_incremental.py
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ def generate_incremental_script(
"pragma solidity ^0.8.0;",
"",
'import {Script} from "forge-std/Script.sol";',
'import {GachaTeamRegistry} from "../src/teams/GachaTeamRegistry.sol";',
'import {GachaTeamRegistry} from "../src/game-layer/GachaTeamRegistry.sol";',
'import {MonStats} from "../src/Structs.sol";',
'import {Type} from "../src/Enums.sol";',
"",
Expand Down
17 changes: 11 additions & 6 deletions processing/validateMoves.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,10 +218,10 @@ def _parse_custom_implementation(self, content: str, contract_data: ContractData
# Look for constant declarations
contract_data.power = self._extract_constant_value(content, 'BASE_POWER')

# Look for accuracy constant (try both DEFAULT_ACCURACY and ACCURACY)
contract_data.accuracy = self._extract_constant_value(content, 'DEFAULT_ACCURACY')
if contract_data.accuracy is None:
contract_data.accuracy = self._extract_constant_value(content, 'ACCURACY')
# Prefer an explicit ACCURACY constant; otherwise infer from DEFAULT_ACCURACY usage in the body
contract_data.accuracy = self._extract_constant_value(content, 'ACCURACY')
if contract_data.accuracy is None and self._references_default_accuracy(content):
contract_data.accuracy = 100

# Look for function implementations
contract_data.stamina = self._extract_function_return_value(content, 'stamina')
Expand All @@ -233,7 +233,7 @@ def _parse_custom_implementation(self, content: str, contract_data: ContractData

def _extract_param_value(self, params_block: str, param_name: str) -> Optional[int]:
"""Extract numeric parameter value from ATTACK_PARAMS block"""
pattern = rf'{param_name}:\s*(\d+)'
pattern = rf'\b{param_name}\b:\s*(\d+)'
match = re.search(pattern, params_block)
return int(match.group(1)) if match else None

Expand Down Expand Up @@ -282,10 +282,15 @@ def _extract_enum_value(self, params_block: str, param_name: str, enum_type: str

def _extract_constant_value(self, content: str, constant_name: str) -> Optional[int]:
"""Extract constant value from contract"""
pattern = rf'{constant_name}\s*=\s*(\d+)'
pattern = rf'\b{constant_name}\b\s*=\s*(\d+)'
match = re.search(pattern, content)
return int(match.group(1)) if match else None

def _references_default_accuracy(self, content: str) -> bool:
"""Check whether the contract body references DEFAULT_ACCURACY (ignoring import lines)"""
body = re.sub(r'^\s*import\s+[^;]+;', '', content, flags=re.MULTILINE)
return re.search(r'\bDEFAULT_ACCURACY\b', body) is not None

def _extract_function_return_value(self, content: str, function_name: str) -> Optional[int]:
"""Extract return value from function implementation"""
# Look for function that returns a constant
Expand Down
11 changes: 10 additions & 1 deletion script/EngineAndPeriphery.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {OkayCPU} from "../src/cpu/OkayCPU.sol";
import {BetterCPU} from "../src/cpu/BetterCPU.sol";
import {ICPURNG} from "../src/rng/ICPURNG.sol";
import {IGachaRNG} from "../src/rng/IGachaRNG.sol";
import {GachaTeamRegistry} from "../src/teams/GachaTeamRegistry.sol";
import {GachaTeamRegistry} from "../src/game-layer/GachaTeamRegistry.sol";
import {TypeCalculator} from "../src/types/TypeCalculator.sol";
import {SignedMatchmaker} from "../src/matchmaker/SignedMatchmaker.sol";
import {BattleHistory} from "../src/hooks/BattleHistory.sol";
Expand Down Expand Up @@ -62,6 +62,15 @@ contract EngineAndPeriphery is Script {
BetterCPU betterCPU = new BetterCPU(GAME_MOVES_PER_MON, engine, ICPURNG(address(0)), typeCalc);
deployedContracts.push(DeployData({name: "BETTER CPU", contractAddress: address(betterCPU)}));

// Whitelist both CPUs so users can setOpponentTeam against them.
{
address[] memory toAllow = new address[](2);
toAllow[0] = address(okayCPU);
toAllow[1] = address(betterCPU);
address[] memory toDisallow = new address[](0);
gachaTeamRegistry.setWhitelistedOpponents(toAllow, toDisallow);
}

SignedMatchmaker signedMatchmaker = new SignedMatchmaker(engine);
deployedContracts.push(DeployData({name: "SIGNED MATCHMAKER", contractAddress: address(signedMatchmaker)}));

Expand Down
2 changes: 1 addition & 1 deletion script/SetupCPU.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pragma solidity ^0.8.0;

import "forge-std/Script.sol";

import {GachaTeamRegistry} from "../src/teams/GachaTeamRegistry.sol";
import {GachaTeamRegistry} from "../src/game-layer/GachaTeamRegistry.sol";

struct DeployData {
string name;
Expand Down
60 changes: 58 additions & 2 deletions script/SetupMons.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
pragma solidity ^0.8.0;

import {Script} from "forge-std/Script.sol";
import {GachaTeamRegistry} from "../src/teams/GachaTeamRegistry.sol";
import {GachaTeamRegistry} from "../src/game-layer/GachaTeamRegistry.sol";
import {MonStats} from "../src/Structs.sol";
import {Type} from "../src/Enums.sol";

Expand Down Expand Up @@ -42,6 +42,10 @@ import {Initialize} from "../src/mons/inutia/Initialize.sol";
import {Interweaving} from "../src/mons/inutia/Interweaving.sol";
import {ActusReus} from "../src/mons/malalien/ActusReus.sol";
import {TripleThink} from "../src/mons/malalien/TripleThink.sol";
import {Adaptor} from "../src/mons/nirvamma/Adaptor.sol";
import {Chronoffense} from "../src/mons/nirvamma/Chronoffense.sol";
import {HardReset} from "../src/mons/nirvamma/HardReset.sol";
import {ModalBolt} from "../src/mons/nirvamma/ModalBolt.sol";
import {Deadlift} from "../src/mons/pengym/Deadlift.sol";
import {DeepFreeze} from "../src/mons/pengym/DeepFreeze.sol";
import {PistolSquat} from "../src/mons/pengym/PistolSquat.sol";
Expand Down Expand Up @@ -73,7 +77,7 @@ contract SetupMons is Script {
GachaTeamRegistry registry = GachaTeamRegistry(vm.envAddress("GACHA_TEAM_REGISTRY"));

// Deploy all mons and collect deployment data
DeployData[][] memory allDeployData = new DeployData[][](12);
DeployData[][] memory allDeployData = new DeployData[][](13);

allDeployData[0] = deployGhouliath(registry);
allDeployData[1] = deployInutia(registry);
Expand All @@ -87,6 +91,7 @@ contract SetupMons is Script {
allDeployData[9] = deployAurox(registry);
allDeployData[10] = deployXmon(registry);
allDeployData[11] = deployEkineki(registry);
allDeployData[12] = deployNirvamma(registry);

// Calculate total length for flattened array
uint256 totalLength = 0;
Expand Down Expand Up @@ -764,4 +769,55 @@ contract SetupMons is Script {
registry.createMon(11, stats, moves, abilities, keys, values);
}

function deployNirvamma(GachaTeamRegistry registry) internal returns (DeployData[] memory) {
DeployData[] memory deployedContracts = new DeployData[](4);

address[4] memory addrs;

{
addrs[0] = address(new HardReset());
deployedContracts[0] = DeployData({name: "Hard Reset", contractAddress: addrs[0]});
}
{
addrs[1] = address(new Chronoffense(StatBoosts(vm.envAddress("STAT_BOOSTS"))));
deployedContracts[1] = DeployData({name: "Chronoffense", contractAddress: addrs[1]});
}
{
addrs[2] = address(new ModalBolt(IEffect(vm.envAddress("BURN_STATUS")), IEffect(vm.envAddress("FROSTBITE_STATUS")), IEffect(vm.envAddress("ZAP_STATUS"))));
deployedContracts[2] = DeployData({name: "Modal Bolt", contractAddress: addrs[2]});
}
{
addrs[3] = address(new Adaptor());
deployedContracts[3] = DeployData({name: "Adaptor", contractAddress: addrs[3]});
}

_registerNirvamma(registry, addrs);

return deployedContracts;
}

function _registerNirvamma(GachaTeamRegistry registry, address[4] memory addrs) internal {
MonStats memory stats = MonStats({
hp: 373,
stamina: 5,
speed: 177,
attack: 202,
defense: 168,
specialAttack: 140,
specialDefense: 202,
type1: Type.Math,
type2: Type.None
});
uint256[] memory moves = new uint256[](4);
moves[0] = uint256(uint160(addrs[0]));
moves[1] = 0x500b314000000000000000000000000000000000000000000000000000000000 | uint256(uint160(vm.envAddress("PANIC_STATUS")));
moves[2] = uint256(uint160(addrs[1]));
moves[3] = uint256(uint160(addrs[2]));
uint256[] memory abilities = new uint256[](1);
abilities[0] = (uint256(1) << 248) | uint256(uint160(addrs[3]));
bytes32[] memory keys = new bytes32[](0);
bytes32[] memory values = new bytes32[](0);
registry.createMon(12, stats, moves, abilities, keys, values);
}

}
2 changes: 2 additions & 0 deletions sims/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
bun.lockb
Loading