Added logic, can still be locked
This commit is contained in:
@@ -52,7 +52,22 @@
|
|||||||
TMP_HOME=$(mktemp -d)
|
TMP_HOME=$(mktemp -d)
|
||||||
HOME=$TMP_HOME ${pkgs.archipelago}/bin/archipelago --update_settings
|
HOME=$TMP_HOME ${pkgs.archipelago}/bin/archipelago --update_settings
|
||||||
ln -s ${nodecames-apworld} "$TMP_HOME/.local/share/Archipelago/worlds/nodecames.apworld"
|
ln -s ${nodecames-apworld} "$TMP_HOME/.local/share/Archipelago/worlds/nodecames.apworld"
|
||||||
HOME=$TMP_HOME ${pkgs.archipelago}/bin/archipelago
|
chmod +w "$TMP_HOME/.local/share/Archipelago/Players/"
|
||||||
|
ln -s ${./src/main/resources/apworld/test.yml} "$TMP_HOME/.local/share/Archipelago/Players/Tester.yaml"
|
||||||
|
HOME=$TMP_HOME ${pkgs.archipelago}/bin/archipelago "$@"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
archipelago-host = pkgs.writeShellApplication {
|
||||||
|
name = "archipelago-host";
|
||||||
|
text = ''
|
||||||
|
TMP_HOME=$(mktemp -d)
|
||||||
|
HOME=$TMP_HOME ${pkgs.archipelago}/bin/archipelago --update_settings
|
||||||
|
ln -s ${nodecames-apworld} "$TMP_HOME/.local/share/Archipelago/worlds/nodecames.apworld"
|
||||||
|
chmod +w "$TMP_HOME/.local/share/Archipelago/Players/"
|
||||||
|
ln -s ${./src/main/resources/apworld/test.yml} "$TMP_HOME/.local/share/Archipelago/Players/Tester.yaml"
|
||||||
|
HOME=$TMP_HOME ${pkgs.archipelago}/bin/archipelago Generate
|
||||||
|
GENFILE=$(find "$TMP_HOME/.local/share/Archipelago/output/" -name "*.zip")
|
||||||
|
HOME=$TMP_HOME ${pkgs.archipelago}/bin/archipelago Host "$GENFILE"
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
in {
|
in {
|
||||||
@@ -66,6 +81,7 @@
|
|||||||
nodecames = { type = "app"; program = "${nodecames-launcher}/bin/nodecames"; };
|
nodecames = { type = "app"; program = "${nodecames-launcher}/bin/nodecames"; };
|
||||||
postgres-server = { type = "app"; program = "${postgres-server}/bin/postgres-server"; };
|
postgres-server = { type = "app"; program = "${postgres-server}/bin/postgres-server"; };
|
||||||
archipelago = { type = "app"; program = "${test-archipelago}/bin/archipelago"; };
|
archipelago = { type = "app"; program = "${test-archipelago}/bin/archipelago"; };
|
||||||
|
archipelago-host = { type = "app"; program = "${archipelago-host}/bin/archipelago-host"; };
|
||||||
};
|
};
|
||||||
|
|
||||||
devShells.${system} = {
|
devShells.${system} = {
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
MAX_WORLD_COUNT=100
|
||||||
|
MAX_WORD_PER_WORLD=99
|
||||||
|
|
||||||
|
def parseWordDistribution(s):
|
||||||
|
components = s.split(';')
|
||||||
|
out = {}
|
||||||
|
for comp in components:
|
||||||
|
p = re.match("^([GWB])([GWB])([0-9]+)$", comp)
|
||||||
|
if not p:
|
||||||
|
raise ValueError("Invalid component of word distribution")
|
||||||
|
colora = p.group(1)
|
||||||
|
colorb = p.group(2)
|
||||||
|
count = int(p.group(3))
|
||||||
|
if (colora + colorb in out):
|
||||||
|
raise ValueError(f"Component {colora}{colorb} defined twice")
|
||||||
|
out[colora + colorb] = count
|
||||||
|
return out
|
||||||
|
|
||||||
|
filler_item = "filler"
|
||||||
|
|
||||||
|
ITEM_NAME_TO_ID = {
|
||||||
|
"progressive_hint_count" : 1,
|
||||||
|
"zero_hint" : 2,
|
||||||
|
"infinity_hint" : 3,
|
||||||
|
"one_more_guess" : 4,
|
||||||
|
"hint_max_length" : 5,
|
||||||
|
"end_guessing" : 6,
|
||||||
|
"only_one_green_green" : 7,
|
||||||
|
"sudden_death" : 8,
|
||||||
|
"structured_grid" : 9,
|
||||||
|
"hints_in_a_row" : 10,
|
||||||
|
"anyone_starts" : 11,
|
||||||
|
"progressive_hint_word_count": 12,
|
||||||
|
"progressive_hint_word_length": 13,
|
||||||
|
"filler": 14
|
||||||
|
}
|
||||||
|
for w in range(MAX_WORLD_COUNT):
|
||||||
|
ITEM_NAME_TO_ID[f"unlock_world_{w+1}"] = 100+w
|
||||||
|
|
||||||
|
def location_id_of_world_and_word(world, word):
|
||||||
|
return 10000 + (world-1)*(MAX_WORD_PER_WORLD+1) + word
|
||||||
|
def world_and_word_of_location(id):
|
||||||
|
world = (id - 10000)//(MAX_WORD_PER_WORLD+1) + 1
|
||||||
|
word = (id - 10000) % (MAX_WORD_PER_WORLD+1)
|
||||||
|
return (world, word)
|
||||||
|
def location_name_of_world_and_word(world, word):
|
||||||
|
return f"World {world} word {word}"
|
||||||
|
|
||||||
|
LOCATION_NAME_TO_ID = {location_name_of_world_and_word(world,word) : location_id_of_world_and_word(world,word) for world in range(1,MAX_WORLD_COUNT+1) for word in range(0,MAX_WORD_PER_WORLD+1)}
|
||||||
+12
-24
@@ -1,46 +1,27 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from BaseClasses import Item, ItemClassification
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .world import NodecamesWorld
|
from .world import NodecamesWorld
|
||||||
|
|
||||||
ITEM_NAME_TO_ID = {
|
from BaseClasses import Item, ItemClassification
|
||||||
"progressive_hint_count" : 1,
|
|
||||||
"zero_hint" : 2,
|
|
||||||
"infinity_hint" : 3,
|
|
||||||
"one_more_guess" : 4,
|
|
||||||
"hint_max_length" : 5,
|
|
||||||
"end_guessing" : 6,
|
|
||||||
"only_one_green_green" : 7,
|
|
||||||
"sudden_death" : 8,
|
|
||||||
"structured_grid" : 9,
|
|
||||||
"hints_in_a_row" : 10,
|
|
||||||
"anyone_starts" : 11,
|
|
||||||
"progressive_hint_word_count": 12,
|
|
||||||
"progressive_hint_word_length": 13,
|
|
||||||
"filler": 14
|
|
||||||
}
|
|
||||||
for w in range(1,12+1):
|
|
||||||
ITEM_NAME_TO_ID[f"unlock_world_{w}"] = 100+w
|
|
||||||
|
|
||||||
|
from . import const as c
|
||||||
|
|
||||||
class NodecamesItem(Item):
|
class NodecamesItem(Item):
|
||||||
game = "Nodecames"
|
game = "Nodecames"
|
||||||
|
|
||||||
def get_random_filler_item_name(world: NodecamesWorld) -> str:
|
def get_random_filler_item_name(world: NodecamesWorld) -> str:
|
||||||
return "filler"
|
return c.filler_item
|
||||||
|
|
||||||
|
|
||||||
def create_item_with_correct_classification(
|
def create_item_with_correct_classification(
|
||||||
world: NodecamesWorld, name: str
|
world: NodecamesWorld, name: str
|
||||||
) -> NodecamesItem:
|
) -> NodecamesItem:
|
||||||
classification = ItemClassification.progression
|
classification = ItemClassification.progression
|
||||||
if name == "filler":
|
if name == c.filler_item:
|
||||||
classification = ItemClassification.filler
|
classification = ItemClassification.filler
|
||||||
return NodecamesItem(name, classification, ITEM_NAME_TO_ID[name], world.player)
|
return NodecamesItem(name, classification, c.ITEM_NAME_TO_ID[name], world.player)
|
||||||
|
|
||||||
|
|
||||||
def create_all_items(world: NodecamesWorld) -> None:
|
def create_all_items(world: NodecamesWorld) -> None:
|
||||||
@@ -64,9 +45,16 @@ def create_all_items(world: NodecamesWorld) -> None:
|
|||||||
world.create_item("hints_in_a_row"),
|
world.create_item("hints_in_a_row"),
|
||||||
world.create_item("anyone_starts"),
|
world.create_item("anyone_starts"),
|
||||||
]
|
]
|
||||||
|
for wi,wn in enumerate(world.options.worlds.keys()):
|
||||||
|
w = world.options.worlds[wn]
|
||||||
|
if w['locked']:
|
||||||
|
itempool.append(world.create_item(f"unlock_world_{wi}"))
|
||||||
|
|
||||||
|
|
||||||
to_complete = len(world.multiworld.get_unfilled_locations(world.player)) - len(itempool)
|
to_complete = len(world.multiworld.get_unfilled_locations(world.player)) - len(itempool)
|
||||||
|
|
||||||
|
print(to_complete,"items to fill")
|
||||||
|
|
||||||
itempool += [world.create_filler() for _ in range(to_complete)]
|
itempool += [world.create_filler() for _ in range(to_complete)]
|
||||||
|
|
||||||
world.multiworld.itempool += itempool
|
world.multiworld.itempool += itempool
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
from BaseClasses import ItemClassification, Location
|
|
||||||
|
|
||||||
from . import items
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from .world import NodecamesWorld
|
|
||||||
|
|
||||||
|
|
||||||
GREEN_COUNT_PER_WORLD = [6, 9, 9, 12, 16, 16, 20, 25, 25, 30, 36, 36]
|
|
||||||
|
|
||||||
def worldName(index):
|
|
||||||
return f"{(index//3)+1}-{(index%3)+1}"
|
|
||||||
|
|
||||||
LOCATION_NAME_TO_ID = {}
|
|
||||||
LOCATION_NAME_FROM_WORLD = {}
|
|
||||||
id = 0
|
|
||||||
for w in range(len(GREEN_COUNT_PER_WORLD)):
|
|
||||||
LOCATION_NAME_FROM_WORLD[w] = []
|
|
||||||
for i in range(1, GREEN_COUNT_PER_WORLD[w]):
|
|
||||||
name = f"World {worldName(w)} word {i}"
|
|
||||||
id += 1
|
|
||||||
LOCATION_NAME_FROM_WORLD[w].append(name)
|
|
||||||
LOCATION_NAME_TO_ID[name] = id
|
|
||||||
name = f"Complete {worldName(w)}"
|
|
||||||
id += 1
|
|
||||||
LOCATION_NAME_FROM_WORLD[w].append(name)
|
|
||||||
LOCATION_NAME_TO_ID[name] = id
|
|
||||||
|
|
||||||
|
|
||||||
class NodecamesLocation(Location):
|
|
||||||
game = "Nodecames"
|
|
||||||
|
|
||||||
|
|
||||||
def create_all_locations(world: NodecamesWorld) -> None:
|
|
||||||
create_world_locations(world)
|
|
||||||
create_events(world)
|
|
||||||
|
|
||||||
|
|
||||||
def create_world_locations(world: NodecamesWorld) -> None:
|
|
||||||
overworld = world.get_region("Overworld")
|
|
||||||
|
|
||||||
for w in LOCATION_NAME_FROM_WORLD:
|
|
||||||
for l in range(len(LOCATION_NAME_FROM_WORLD[w])-1):
|
|
||||||
name = LOCATION_NAME_FROM_WORLD[w][l]
|
|
||||||
loc = NodecamesLocation(
|
|
||||||
world.player,
|
|
||||||
name,
|
|
||||||
LOCATION_NAME_TO_ID[name],
|
|
||||||
overworld,
|
|
||||||
)
|
|
||||||
overworld.locations.append(loc)
|
|
||||||
|
|
||||||
def all_completion_items(world: NodecamesWorld) -> list[str]:
|
|
||||||
return [LOCATION_NAME_FROM_WORLD[w][-1]+" item" for w in LOCATION_NAME_FROM_WORLD]
|
|
||||||
|
|
||||||
def create_events(world: NodecamesWorld) -> None:
|
|
||||||
overworld = world.get_region("Overworld")
|
|
||||||
for w in LOCATION_NAME_FROM_WORLD:
|
|
||||||
overworld.add_event(
|
|
||||||
LOCATION_NAME_FROM_WORLD[w][-1],
|
|
||||||
LOCATION_NAME_FROM_WORLD[w][-1]+" item",
|
|
||||||
location_type = NodecamesLocation,
|
|
||||||
item_type = items.NodecamesItem)
|
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from Options import Choice, OptionGroup, PerGameCommonOptions, Range, DefaultOnToggle
|
from Options import Choice, OptionGroup, PerGameCommonOptions, Range, DefaultOnToggle, OptionDict
|
||||||
|
from schema import Schema, And
|
||||||
|
from . import const as c
|
||||||
|
|
||||||
class WorldsLocked(DefaultOnToggle):
|
class WorldsLocked(DefaultOnToggle):
|
||||||
"""
|
"""
|
||||||
@@ -10,6 +11,16 @@ class WorldsLocked(DefaultOnToggle):
|
|||||||
|
|
||||||
display_name = "Worlds locked"
|
display_name = "Worlds locked"
|
||||||
|
|
||||||
|
class HintWordLogicRatio(Range):
|
||||||
|
"""
|
||||||
|
How many hints per word count in the grid is needed as a minimum.
|
||||||
|
Used to compute the logic
|
||||||
|
Divide by 10
|
||||||
|
"""
|
||||||
|
range_start = 10
|
||||||
|
range_end = 80
|
||||||
|
display_name = "Hint Word Logic Ration"
|
||||||
|
|
||||||
|
|
||||||
class StartHintCount(Range):
|
class StartHintCount(Range):
|
||||||
"""
|
"""
|
||||||
@@ -183,12 +194,29 @@ class VictoryCondition(Choice):
|
|||||||
option_LastWorld = 2
|
option_LastWorld = 2
|
||||||
default = 2
|
default = 2
|
||||||
|
|
||||||
|
class WorldsOption(OptionDict):
|
||||||
|
"""
|
||||||
|
Which nodecames worlds will be in the game and their options
|
||||||
|
|
||||||
|
It takes the following parameters:
|
||||||
|
|
||||||
|
locked: wether the world is unlocked by default, or needs an item to be unlocked
|
||||||
|
word_distribution: a string describing the distributions of colors on the grid
|
||||||
|
"""
|
||||||
|
display_name = "Worlds"
|
||||||
|
schema = Schema({
|
||||||
|
str: {
|
||||||
|
"locked": bool,
|
||||||
|
"word_distribution": And(str, c.parseWordDistribution),
|
||||||
|
"required": bool
|
||||||
|
}})
|
||||||
|
|
||||||
# We must now define a dataclass inheriting from PerGameCommonOptions that we put all our options in.
|
# We must now define a dataclass inheriting from PerGameCommonOptions that we put all our options in.
|
||||||
# This is in the format "option_name_in_snake_case: OptionClassName".
|
# This is in the format "option_name_in_snake_case: OptionClassName".
|
||||||
@dataclass
|
@dataclass
|
||||||
class NodecamesOptions(PerGameCommonOptions):
|
class NodecamesOptions(PerGameCommonOptions):
|
||||||
worlds_locked: WorldsLocked
|
worlds_locked: WorldsLocked
|
||||||
|
hint_word_logic_ratio: HintWordLogicRatio
|
||||||
victory_condition: VictoryCondition
|
victory_condition: VictoryCondition
|
||||||
start_hint_count: StartHintCount
|
start_hint_count: StartHintCount
|
||||||
max_hint_count: MaxHintCount
|
max_hint_count: MaxHintCount
|
||||||
@@ -205,14 +233,14 @@ class NodecamesOptions(PerGameCommonOptions):
|
|||||||
structured_grid: StructuredGrid
|
structured_grid: StructuredGrid
|
||||||
hints_in_a_row: HintsInARow
|
hints_in_a_row: HintsInARow
|
||||||
anyone_starts: AnyoneStarts
|
anyone_starts: AnyoneStarts
|
||||||
world_count: WorldCount
|
worlds: WorldsOption
|
||||||
|
|
||||||
|
|
||||||
# If we want to group our options by similar type, we can do so as well. This looks nice on the website.
|
# If we want to group our options by similar type, we can do so as well. This looks nice on the website.
|
||||||
option_groups = [
|
option_groups = [
|
||||||
OptionGroup(
|
OptionGroup(
|
||||||
"General Options",
|
"General Options",
|
||||||
[WorldsLocked, VictoryCondition],
|
[WorldsLocked, VictoryCondition, HintWordLogicRatio],
|
||||||
),
|
),
|
||||||
OptionGroup(
|
OptionGroup(
|
||||||
"Grid Options",
|
"Grid Options",
|
||||||
@@ -232,12 +260,13 @@ option_groups = [
|
|||||||
AnyoneStarts,
|
AnyoneStarts,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
OptionGroup("Worlds Options", [WorldCount]),
|
OptionGroup("Worlds Options", [WorldsOption]),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Finally, we can define some option presets if we want the player to be able to quickly choose a specific "mode".
|
# Finally, we can define some option presets if we want the player to be able to quickly choose a specific "mode".
|
||||||
option_presets = {
|
option_presets = {
|
||||||
"default": {
|
"default": {
|
||||||
|
"hint_word_logic_ratio": 25,
|
||||||
"worlds_locked": True,
|
"worlds_locked": True,
|
||||||
"start_hint_count": 1,
|
"start_hint_count": 1,
|
||||||
"max_hint_count": 10,
|
"max_hint_count": 10,
|
||||||
@@ -254,7 +283,56 @@ option_presets = {
|
|||||||
"structured_grid": 1,
|
"structured_grid": 1,
|
||||||
"hints_in_a_row": 1,
|
"hints_in_a_row": 1,
|
||||||
"anyone_starts": 1,
|
"anyone_starts": 1,
|
||||||
"world_count": 12,
|
"worlds": {
|
||||||
|
"1-1": {
|
||||||
|
"locked": False,
|
||||||
|
"word_distribution": "GG1;GW2;GB0;WG2;WW1;WB0;BG0;BW0;BB0"
|
||||||
|
},
|
||||||
|
"1-2": {
|
||||||
|
"locked": True,
|
||||||
|
"word_distribution": "GG2;GW1;GB1;WG1;WW2;WB0;BG1;BW0;BB1"
|
||||||
|
},
|
||||||
|
"1-3": {
|
||||||
|
"locked": True,
|
||||||
|
"word_distribution": "GG2;GW1;GB1;WG1;WW2;WB0;BG1;BW0;BB1"
|
||||||
|
},
|
||||||
|
"2-1": {
|
||||||
|
"locked": True,
|
||||||
|
"word_distribution": "GG2;GW1;GB1;WG1;WW3;WB1;BG1;BW1;BB1"
|
||||||
|
},
|
||||||
|
"2-2": {
|
||||||
|
"locked": True,
|
||||||
|
"word_distribution": "GG2;GW2;GB1;WG2;WW5;WB1;BG1;BW1;BB1"
|
||||||
|
},
|
||||||
|
"2-3": {
|
||||||
|
"locked": True,
|
||||||
|
"word_distribution": "GG2;GW2;GB1;WG2;WW5;WB1;BG1;BW1;BB1"
|
||||||
|
},
|
||||||
|
"3-1": {
|
||||||
|
"locked": True,
|
||||||
|
"word_distribution": "GG2;GW4;GB1;WG4;WW5;WB1;BG1;BW1;BB1"
|
||||||
|
},
|
||||||
|
"3-2": {
|
||||||
|
"locked": True,
|
||||||
|
"word_distribution": "GG3;GW5;GB1;WG5;WW7;WB1;BG1;BW1;BB1"
|
||||||
|
},
|
||||||
|
"3-3": {
|
||||||
|
"locked": True,
|
||||||
|
"word_distribution": "GG3;GW5;GB1;WG5;WW7;WB1;BG1;BW1;BB1"
|
||||||
|
},
|
||||||
|
"4-1": {
|
||||||
|
"locked": True,
|
||||||
|
"word_distribution": "GG3;GW5;GB2;WG5;WW7;WB2;BG2;BW2;BB1"
|
||||||
|
},
|
||||||
|
"4-2": {
|
||||||
|
"locked": True,
|
||||||
|
"word_distribution": "GG5;GW6;GB2;WG6;WW9;WB2;BG2;BW2;BB2"
|
||||||
|
},
|
||||||
|
"4-3": {
|
||||||
|
"locked": True,
|
||||||
|
"word_distribution": "GG5;GW6;GB2;WG6;WW9;WB2;BG2;BW2;BB2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"victory_condition": 2,
|
"victory_condition": 2,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
from BaseClasses import Entrance, Region
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from .world import NodecamesWorld
|
|
||||||
|
|
||||||
|
|
||||||
def create_and_connect_regions(world: NodecamesWorld) -> None:
|
|
||||||
create_all_regions(world)
|
|
||||||
connect_regions(world)
|
|
||||||
|
|
||||||
|
|
||||||
def create_all_regions(world: NodecamesWorld) -> None:
|
|
||||||
overworld = Region("Overworld", world.player, world.multiworld)
|
|
||||||
world.multiworld.regions += [overworld]
|
|
||||||
|
|
||||||
|
|
||||||
def connect_regions(world: NodecamesWorld) -> None:
|
|
||||||
overworld = world.get_region("Overworld")
|
|
||||||
+64
-17
@@ -1,31 +1,78 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from rule_builder.options import OptionFilter
|
|
||||||
from rule_builder.rules import Has, HasAll, Rule
|
|
||||||
|
|
||||||
from . import locations
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .world import NodecamesWorld
|
from .world import NodecamesWorld
|
||||||
|
|
||||||
|
|
||||||
def set_all_rules(world: NodecamesWorld) -> None:
|
from rule_builder.options import OptionFilter
|
||||||
set_all_entrance_rules(world)
|
from rule_builder.rules import Has, HasAll, Rule, True_
|
||||||
set_all_location_rules(world)
|
from BaseClasses import Region, Location
|
||||||
set_completion_condition(world)
|
from . import const as c
|
||||||
|
import math
|
||||||
|
from . import items
|
||||||
|
|
||||||
|
def print_graph(world : NodecamesWorld):
|
||||||
|
for region in world.multiworld.regions:
|
||||||
|
print(region)
|
||||||
|
for exit in region.exits:
|
||||||
|
print("->",exit,"--",exit.access_rule)
|
||||||
|
|
||||||
def set_all_entrance_rules(world: NodecamesWorld) -> None:
|
class NodecamesLocation(Location):
|
||||||
pass
|
game = "Nodecames"
|
||||||
|
|
||||||
|
def total_word_count(distribution):
|
||||||
|
return distribution['GB']+distribution['BG']+distribution['GW']+distribution['WG']+distribution['GG']
|
||||||
|
|
||||||
def set_all_location_rules(world: NodecamesWorld) -> None:
|
def can_access_word(options, word):
|
||||||
pass
|
ratio = options.hint_word_logic_ratio/10
|
||||||
|
min_hint_count = math.ceil(word/ratio)
|
||||||
|
min_hint_word_count = math.ceil(word/min_hint_count) # min_hint_count * min_hint_word_count >= word
|
||||||
|
|
||||||
|
return Has("progressive_hint_word_count", count=max(min_hint_word_count-options.start_hint_word_count,0)) & Has("progressive_hint_count", count=max(min_hint_count-options.start_hint_count,0))
|
||||||
|
|
||||||
def set_completion_condition(world: NodecamesWorld) -> None:
|
def can_access_world(options, wn, wi):
|
||||||
|
if options.worlds[wn]['locked']:
|
||||||
|
return Has(f"unlock_world_{wi}")
|
||||||
|
else:
|
||||||
|
return True_()
|
||||||
|
|
||||||
worldLocations = locations.all_completion_items(world)
|
def create_all_regions(world: NodecamesWorld) -> None:
|
||||||
world.set_completion_rule(HasAll(*worldLocations))
|
#TODO make a helper to add new regions
|
||||||
|
owmap = Region("Map", world.player, world.multiworld)
|
||||||
|
|
||||||
|
wregions = dict()
|
||||||
|
toComplete = list()
|
||||||
|
|
||||||
|
for wi,wn in enumerate(world.options.worlds.keys()):
|
||||||
|
w = world.options.worlds[wn]
|
||||||
|
distrib = c.parseWordDistribution(w['word_distribution'])
|
||||||
|
wregions[wn] = list()
|
||||||
|
for i in range(total_word_count(distrib)+1):
|
||||||
|
name = c.location_name_of_world_and_word(wi+1,i)
|
||||||
|
wregions[wn].append(Region(name, world.player, world.multiworld))
|
||||||
|
if i>0:
|
||||||
|
wregions[wn][i-1].connect(wregions[wn][i], rule = can_access_word(world.options, i))
|
||||||
|
else:
|
||||||
|
owmap.connect(wregions[wn][i], rule = can_access_world(world.options, wn, wi))
|
||||||
|
loc = NodecamesLocation(
|
||||||
|
world.player,
|
||||||
|
name,
|
||||||
|
c.LOCATION_NAME_TO_ID[name],
|
||||||
|
wregions[wn][i]
|
||||||
|
)
|
||||||
|
wregions[wn][i].locations.append(loc)
|
||||||
|
wregions[wn][-1].add_event(
|
||||||
|
f"World {wn} complete",
|
||||||
|
f"World {wn} completion item",
|
||||||
|
location_type = NodecamesLocation,
|
||||||
|
item_type = items.NodecamesItem)
|
||||||
|
if w['required']:
|
||||||
|
toComplete.append(f"World {wn} completion item")
|
||||||
|
|
||||||
|
world.set_completion_rule(HasAll(*toComplete))
|
||||||
|
|
||||||
|
allregions = [owmap] + [wrxv for wrv in wregions.values() for wrxv in wrv]
|
||||||
|
print(f"Generated {len(allregions)} regions")
|
||||||
|
print(f"Generated {sum([len(reg.locations) for reg in allregions])} locations")
|
||||||
|
world.multiworld.regions += allregions
|
||||||
@@ -3,9 +3,9 @@ from typing import Any
|
|||||||
|
|
||||||
from worlds.AutoWorld import World
|
from worlds.AutoWorld import World
|
||||||
|
|
||||||
from . import items, locations, regions, rules
|
from . import items, rules
|
||||||
from . import options as nodecames_options
|
from . import options as nodecames_options
|
||||||
|
from . import const as c
|
||||||
|
|
||||||
class NodecamesWorld(World):
|
class NodecamesWorld(World):
|
||||||
"""
|
"""
|
||||||
@@ -17,17 +17,14 @@ class NodecamesWorld(World):
|
|||||||
options_dataclass = nodecames_options.NodecamesOptions
|
options_dataclass = nodecames_options.NodecamesOptions
|
||||||
options: nodecames_options.NodecamesOptions
|
options: nodecames_options.NodecamesOptions
|
||||||
|
|
||||||
location_name_to_id = locations.LOCATION_NAME_TO_ID
|
location_name_to_id = c.LOCATION_NAME_TO_ID
|
||||||
item_name_to_id = items.ITEM_NAME_TO_ID
|
item_name_to_id = c.ITEM_NAME_TO_ID
|
||||||
|
|
||||||
origin_region_name = "Overworld"
|
origin_region_name = "Map"
|
||||||
|
|
||||||
|
# Create regions, locations and rules
|
||||||
def create_regions(self) -> None:
|
def create_regions(self) -> None:
|
||||||
regions.create_and_connect_regions(self)
|
rules.create_all_regions(self)
|
||||||
locations.create_all_locations(self)
|
|
||||||
|
|
||||||
def set_rules(self) -> None:
|
|
||||||
rules.set_all_rules(self)
|
|
||||||
|
|
||||||
def create_items(self) -> None:
|
def create_items(self) -> None:
|
||||||
items.create_all_items(self)
|
items.create_all_items(self)
|
||||||
@@ -40,27 +37,10 @@ class NodecamesWorld(World):
|
|||||||
def get_filler_item_name(self) -> str:
|
def get_filler_item_name(self) -> str:
|
||||||
return items.get_random_filler_item_name(self)
|
return items.get_random_filler_item_name(self)
|
||||||
|
|
||||||
|
# Generates the output file
|
||||||
def generate_output(self, outdir : str) -> None:
|
def generate_output(self, outdir : str) -> None:
|
||||||
# Generates the output file
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Data sent to the client
|
# Data sent to the client
|
||||||
def fill_slot_data(self) -> Mapping[str, Any]:
|
def fill_slot_data(self) -> Mapping[str, Any]:
|
||||||
return self.options.as_dict(
|
return []
|
||||||
"worlds_locked",
|
|
||||||
"start_hint_count",
|
|
||||||
"max_hint_count",
|
|
||||||
"start_hint_length",
|
|
||||||
"max_hint_length",
|
|
||||||
"zero_hint",
|
|
||||||
"infinity_hint",
|
|
||||||
"one_more_guess",
|
|
||||||
"end_guessing",
|
|
||||||
"only_one_green_green",
|
|
||||||
"sudden_death",
|
|
||||||
"structured_grid",
|
|
||||||
"hints_in_a_row",
|
|
||||||
"anyone_starts",
|
|
||||||
"world_count",
|
|
||||||
"victory_condition",
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
name: Tester
|
||||||
|
game: nodecames
|
||||||
|
nodecames:
|
||||||
|
progression_balancing: normal
|
||||||
|
accessibility: full
|
||||||
|
hint_word_logic_ratio: 25
|
||||||
|
|
||||||
|
start_hint_count: 1
|
||||||
|
max_hint_count: 10
|
||||||
|
start_hint_length: 3
|
||||||
|
max_hint_length: 12
|
||||||
|
start_hint_word_count: 1
|
||||||
|
max_hint_word_count: 16
|
||||||
|
|
||||||
|
worlds:
|
||||||
|
1-1:
|
||||||
|
locked: false
|
||||||
|
word_distribution: "GG1;GW2;GB0;WG2;WW1;WB0;BG0;BW0;BB0"
|
||||||
|
required: false
|
||||||
|
1-2:
|
||||||
|
locked: true
|
||||||
|
word_distribution: "GG2;GW1;GB1;WG1;WW2;WB0;BG1;BW0;BB1"
|
||||||
|
required: false
|
||||||
|
1-3:
|
||||||
|
locked: true
|
||||||
|
word_distribution: "GG2;GW1;GB1;WG1;WW2;WB0;BG1;BW0;BB1"
|
||||||
|
required: true
|
||||||
|
2-1:
|
||||||
|
locked: true
|
||||||
|
word_distribution: "GG2;GW1;GB1;WG1;WW3;WB1;BG1;BW1;BB1"
|
||||||
|
required: false
|
||||||
|
2-2:
|
||||||
|
locked: true
|
||||||
|
word_distribution: "GG2;GW2;GB1;WG2;WW5;WB1;BG1;BW1;BB1"
|
||||||
|
required: false
|
||||||
|
2-3:
|
||||||
|
locked: true
|
||||||
|
word_distribution: "GG2;GW2;GB1;WG2;WW5;WB1;BG1;BW1;BB1"
|
||||||
|
required: true
|
||||||
|
3-1:
|
||||||
|
locked: true
|
||||||
|
word_distribution: "GG2;GW4;GB1;WG4;WW5;WB1;BG1;BW1;BB1"
|
||||||
|
required: false
|
||||||
|
3-2:
|
||||||
|
locked: true
|
||||||
|
word_distribution: "GG3;GW5;GB1;WG5;WW7;WB1;BG1;BW1;BB1"
|
||||||
|
required: false
|
||||||
|
3-3:
|
||||||
|
locked: true
|
||||||
|
word_distribution: "GG3;GW5;GB1;WG5;WW7;WB1;BG1;BW1;BB1"
|
||||||
|
required: true
|
||||||
|
4-1:
|
||||||
|
locked: true
|
||||||
|
word_distribution: "GG3;GW5;GB2;WG5;WW7;WB2;BG2;BW2;BB1"
|
||||||
|
required: false
|
||||||
|
4-2:
|
||||||
|
locked: true
|
||||||
|
word_distribution: "GG5;GW6;GB2;WG6;WW9;WB2;BG2;BW2;BB2"
|
||||||
|
required: false
|
||||||
|
4-3:
|
||||||
|
locked: true
|
||||||
|
word_distribution: "GG5;GW6;GB2;WG6;WW9;WB2;BG2;BW2;BB2"
|
||||||
|
required: true
|
||||||
Reference in New Issue
Block a user