Adding first apworld

This commit is contained in:
Mysaa Java
2026-09-26 02:15:02 +02:00
parent 90f3f7afb0
commit 89cac6a0c6
9 changed files with 493 additions and 1 deletions
+1
View File
@@ -0,0 +1 @@
from .world import NodecamesWorld
+6
View File
@@ -0,0 +1,6 @@
{
"game": "NodeCames",
"minimum_ap_version": "0.6.8",
"world_version": "1.0.0",
"authors": ["Mysaa"]
}
+63
View File
@@ -0,0 +1,63 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from BaseClasses import Item, ItemClassification
if TYPE_CHECKING:
from .world import NodecamesWorld
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,
"filler": 12
}
for w in range(1,12+1):
ITEM_NAME_TO_ID[f"unlock_world_{w}"] = 100+w
class NodecamesItem(Item):
game = "Nodecames"
def get_random_filler_item_name(world: NodecamesWorld) -> str:
return "filler"
def create_item_with_correct_classification(
world: NodecamesWorld, name: str
) -> NodecamesItem:
return NodecamesItem(name, ItemClassification.progression, ITEM_NAME_TO_ID[name], world.player)
def create_all_items(world: NodecamesWorld) -> None:
itempool: list[Item] = []
hintItemCount = world.options.max_hint_count - world.options.max_hint_length
itempool += [world.create_item("progressive_hint_count") for _ in range(hintItemCount)]
itempool += [
world.create_item("zero_hint"),
world.create_item("infinity_hint"),
world.create_item("one_more_guess"),
world.create_item("hint_max_length"),
world.create_item("end_guessing"),
world.create_item("only_one_green_green"),
world.create_item("sudden_death"),
world.create_item("structured_grid"),
world.create_item("hints_in_a_row"),
world.create_item("anyone_starts"),
]
to_complete = len(world.multiworld.get_unfilled_locations(world.player)) - len(itempool)
itempool += [world.create_filler() for _ in range(to_complete)]
world.multiworld.itempool += itempool
+67
View File
@@ -0,0 +1,67 @@
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)
+233
View File
@@ -0,0 +1,233 @@
from dataclasses import dataclass
from Options import Choice, OptionGroup, PerGameCommonOptions, Range, DefaultOnToggle
class WorldsLocked(DefaultOnToggle):
"""
Access to worlds need to be unlocked by an item
"""
display_name = "Worlds locked"
class StartHintCount(Range):
"""
Hint count at the beginning of the game
"""
display_name = "Start hint count"
range_start = 1
range_end = 20
default = 1
class MaxHintCount(Range):
"""
Maximum hint count we can get
"""
display_name = "Max hint count"
range_start = 1
range_end = 20
default = 10
class StartHintLength(Range):
"""
Maximal Hint length at the beginning of the game
"""
display_name = "Start Hint Length"
range_start = 3
range_end = 30
default = 3
class MaxHintLength(Range):
"""
Maximal maximum hint length we can get. Once it is reached, maximal length is now infinity.
"""
display_name = "Max Hint Length"
range_start = 3
range_end = 30
default = 10
class OnOffDefault(Choice):
option_Off = 0
option_Item = 1
option_On = 2
default = 1
class ZeroHint(OnOffDefault):
"""
Can give zero as a hint
"""
display_name = "Hint Count 0"
class InfinityHint(OnOffDefault):
"""
Can give infinity as a hint
"""
display_name = "Hint Count infty"
class OneMoreGuess(OnOffDefault):
"""
Can guess one word more than hint count
"""
display_name = "One More Guess"
class EndGuessing(OnOffDefault):
"""
Can end guessing before touching the given hint count
"""
display_name = "End Guessing"
class OnlyOneGreenGreen(OnOffDefault):
"""
Green-Green cards only have to be pointed by one of the players
"""
display_name = "Only One Green Green"
class SuddenDeath(OnOffDefault):
"""
Sudden death triggered when no more hints can be given
"""
display_name = "Sudden Death"
class StructuredGrid(OnOffDefault):
"""
Are front and back colors of the cards linked
"""
display_name = "Structured Grid"
class HintsInARow(OnOffDefault):
"""
Players can give multiple hints in a row
"""
display_name = "HintsInARow"
class AnyoneStarts(OnOffDefault):
"""
Both players can start the game
"""
display_name = "AnyoneStarts"
class WorldCount(Range):
"""
How many worlds to generate
"""
display_name = "World Count"
range_start = 1
range_end = 12
default = 12
class VictoryCondition(Choice):
"""
What is needed to complete game
"""
option_AllWorlds = 1
option_LastWorld = 2
default = 2
# 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".
@dataclass
class NodecamesOptions(PerGameCommonOptions):
worlds_locked: WorldsLocked
victory_condition: VictoryCondition
start_hint_count: StartHintCount
max_hint_count: MaxHintCount
start_hint_length: StartHintLength
max_hint_length: MaxHintLength
zero_hint: ZeroHint
infinity_hint: InfinityHint
one_more_guess: OneMoreGuess
end_guessing: EndGuessing
only_one_green_green: OnlyOneGreenGreen
sudden_death: SuddenDeath
structured_grid: StructuredGrid
hints_in_a_row: HintsInARow
anyone_starts: AnyoneStarts
world_count: WorldCount
# If we want to group our options by similar type, we can do so as well. This looks nice on the website.
option_groups = [
OptionGroup(
"General Options",
[WorldsLocked, VictoryCondition],
),
OptionGroup(
"Grid Options",
[
StartHintCount,
MaxHintCount,
StartHintLength,
MaxHintLength,
ZeroHint,
InfinityHint,
OneMoreGuess,
EndGuessing,
OnlyOneGreenGreen,
SuddenDeath,
StructuredGrid,
HintsInARow,
AnyoneStarts,
],
),
OptionGroup("Worlds Options", [WorldCount]),
]
# Finally, we can define some option presets if we want the player to be able to quickly choose a specific "mode".
option_presets = {
"default": {
"worlds_locked": True,
"start_hint_count": 1,
"max_hint_count": 10,
"start_hint_length": 3,
"max_hint_length": 12,
"zero_hint": 1,
"infinity_hint": 1,
"one_more_guess": 1,
"end_guessing": 1,
"only_one_green_green": 1,
"sudden_death": 1,
"structured_grid": 1,
"hints_in_a_row": 1,
"anyone_starts": 1,
"world_count": 12,
"victory_condition": 2,
},
}
+22
View File
@@ -0,0 +1,22 @@
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")
+31
View File
@@ -0,0 +1,31 @@
from __future__ import annotations
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:
from .world import NodecamesWorld
def set_all_rules(world: NodecamesWorld) -> None:
set_all_entrance_rules(world)
set_all_location_rules(world)
set_completion_condition(world)
def set_all_entrance_rules(world: NodecamesWorld) -> None:
pass
def set_all_location_rules(world: NodecamesWorld) -> None:
pass
def set_completion_condition(world: NodecamesWorld) -> None:
worldLocations = locations.all_completion_items(world)
world.set_completion_rule(HasAll(*worldLocations))
+62
View File
@@ -0,0 +1,62 @@
from collections.abc import Mapping
from typing import Any
from worlds.AutoWorld import World
from . import items, locations, regions, rules
from . import options as nodecames_options
class NodecamesWorld(World):
"""
Nodecames is a word game, inspired by Code Names by Vlaada Chvátil
"""
game = "Nodecames"
options_dataclass = nodecames_options.NodecamesOptions
options: nodecames_options.NodecamesOptions
location_name_to_id = locations.LOCATION_NAME_TO_ID
item_name_to_id = items.ITEM_NAME_TO_ID
origin_region_name = "Overworld"
def create_regions(self) -> None:
regions.create_and_connect_regions(self)
locations.create_all_locations(self)
def set_rules(self) -> None:
rules.set_all_rules(self)
def create_items(self) -> None:
items.create_all_items(self)
# Create an item from name
def create_item(self, name: str) -> items.NodecamesItem:
return items.create_item_with_correct_classification(self, name)
# Create one filler item
def get_filler_item_name(self) -> str:
return items.get_random_filler_item_name(self)
# Data sent to the client
def fill_slot_data(self) -> Mapping[str, Any]:
return self.options.as_dict(
"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",
)