Added notes from Troy, fixed minor message bugs

This commit is contained in:
Kayne Ruse 2025-04-03 18:59:09 +11:00
parent a4c112ce7b
commit 7ff5d488d7
7 changed files with 145 additions and 62 deletions

View File

@ -6,7 +6,7 @@ Each time you start a run, a genre is chosen from a predefined list of **Dimensi
Some recurring elements will exist in all dimensions to provide some stability, but will often be warped to fit - for example, if the "fantasy" dimension has Cid, the dwarven shopkeeper, the "scifi" dimension has C.I.D., the Customer Inventory Droid.
Here's a few potential options, which will absolutely change over time:
Here's a few potential options, which absolutely will change over time:
* Sword and Sorcery (LotR, D&D)
* GitS/Neuromancer/Digitized Minds
@ -20,7 +20,6 @@ Here's a few potential options, which will absolutely change over time:
* Stargates/Sliders
* Isekai Protag Syndrome
* Gunslingers (Wild West)
* Zombies/Undead Contagion Plague that infects other dimensions
## Links And Resources

View File

@ -1,4 +1,10 @@
# Dev Notes, Concepts, and Ideas
# Dev Notes
This file is a kind of scratch-pad for a multitude of ideas, that I can't implement yet. They'll be added to, refined or removed over time, so don't get too attached.
TODO: Plan out each confirmed dimension.
## Concepts and Ideas
Color-based effects or biomes that only have an impact when in the FOV?
The "obscurity" is a random value set at the beginning of a run, and lost upon death. The game will have various secrets, some of which are only accessible via certain obscurity values.
@ -10,10 +16,10 @@
It could be interesting, not necssarily a good or fun idea, but if you had another "point" or currency. Where if you spend the first one you get some kind of "cursed knowledge" point and that affects the run somehow? So the more you use your forbidden knowledge the more weird things get?
If you use IRL time and date as a mechanic, go big or go home. Maybe the horror dimension is only accessible during full/new moons?
Should the selected dimension be known at the start of a run?
Could have "modifiers" that apply to existing demensions, altering how they generate i.e. a zombie virus could spread.
Could have "travellers" that move from one dimension to another i.e. want a particularly powerful item? Better hope the "merchant ship" is in this world.
## Healing
While not necessarily "realistic" some are more fun or interesting.
## Healing Items
potion of healing / greater healing / full healing
nano cells (nanophage)
@ -30,9 +36,7 @@ While not necessarily "realistic" some are more fun or interesting.
## Melee Weapons
As some settings are very similar you will see similar or even repeated items. Some can be interchangeable without too much trouble.
Weapons are present here are common / uncommon - rare / God like - super experimental one of a kind.
Weapons are present here are common / uncommon - rare / God like - super experimental one of a kind.
1. sword / club / axe
2. Blessed / holy Weapons / mater worked / ancestors Weapons ect.
@ -68,9 +72,7 @@ Weapons are present here are common / uncommon - rare / God like - super experim
## Ranged Weapons
As some settings are very similar you will see similar or even repeated items. Some can be interchangeable without too much trouble.
Weapons are present here are common / uncommon - rare / God like - super experimental one of a kind.
Weapons are present here are common / uncommon - rare / God like - super experimental one of a kind.
1. Bow / cross bow / sling / javelins ect.
2. Magically enchanted weapons and or ammunition / holy / master worked / Ancestors.
@ -80,7 +82,6 @@ Weapons are present here are common / uncommon - rare / God like - super experim
2. Branded Gucci guns / Military grade / bespoke special editions / specifically designed ammo (armour piecing / hollow point ect.)
3. Tesla hyper beam cannon / experimental thematic manipulation ray / gravidic conversion stream / anti reality fabric hacker / spontaneous combustion field / magnetic ion atom smasher.
1. Common crystal construction / home made / fragile.
2. Antique construction of past generations / sturdy construction / multiple firing chambers / more exotic and rare earth minerals.
3. Pure untarnished faultless crystal or gem construction / soul gem that contains ally or enemy soul / pure unstable quantum core power supply.
@ -89,8 +90,65 @@ Weapons are present here are common / uncommon - rare / God like - super experim
2. Old world weapons / new condition or recently recovered / mint condition or cannibalised guns rifles ect. Specially designed ammunition.
3. Pre war experimental weapons (see cyberpunk)
1. Standard pistol / hidden dart / watch laser ( low power)
2. Military grade pistol / shotgun / machine gun / well maintained / polished / laser watch (med power)
3. One of a kind Q weapons / Anti material rife with special ammunition. / multi barrel rocket launcher with programed ammunition. Laser watch (high)
1. Ref: 1 standard fantasy with Greek twist. Eg.
2. Falcata (sword)
3. Javelins of zues. Club of hercules ect.
1. Old / rusty / poor construction / poorly maintained - fire arms.
2. Well maintained / military grade / new / perfect condition / blessed elephant gun / Tommy gun / crank handle gattling gun.
3. Elder brain warp crystal ammunition / elder God touched ray gun / liminal reality beam conducton array /Finger of unsherlubb' the deep one.
1. Old / dirty / early generation firearms
2. Standard military grade / extra magazine / upgrades (sites / pistol grip / optics) spec ammo / extended magazine.
3. Cutting edge technology / one of a kind experimental weapons/ goa 'uld serpent pistol "zat" (pair / duel ) staff of light (moct toe)
Heavy moct toe (requires tripod of very high strength)
1. Ref: 1 Standard fantasy.
1. Old / rusty / poor construction / poorly maintained - fire arms.
2. Well maintained / military grade / new / perfect condition / blessed/ repeating / blessed tomahawk / crank handle gattling gun.
3. Vatican relic / totem of the tribe / man portable cannon with special ammunition.
## Armour
1. Leather / light / natural.
2. Medium / metal / chain.
3. Full plate / dragon hide / God touched.
1. Stab resist / metal / last generation.
2. Bullet resistant / Stab proof / ceramic plate / Gucci brand.
3. Sub dermal nano plates / white platelet turbo generator / lizard DNA regeneration injection / kenetic absorption and focal tech.
1. Leather / flawed crystal construction / battle damaged / silk mix.
2. Pure silk / Exotic rare crystal construction / master crafted.
3. Amulet of the core mother / light shield energy projector for the fall / sentient orb of protection (sep)
1. Rusty / old / poor construction.
2. Pre war / military grade / mint condition.
3. Power armour / stealth suit / Tesla lighting attraction plate.
See 1 generic fantasy.
1. Stab resist / metal / last generation.
2. Bullet resistant / Stab proof / ceramic plate / Gucci brand.
3. One of a kind Q armour / watch of bullet reputation / attraction / " golden eye" EMP emitter.
1. Leather / light / natural.
2. Medium / metal / bronze.
3. Zues blessed / shield of reflection / lion of nemmedia cloak.
1. 1. Leather / light / natural. Silk mix.
2. Medium / metal / pure silk.
3. Full plate heirloom construct / Amulet of otrallort / slime of the elder beast / mirror of the ever changing staircase.
## Narrative Goals
1. Defeat villian / kill all x in area.
2. Collection of item(s)
3. Activation or deactivate grid / power supply / ritual.
3. Activation or deactivate grid / power supply / ritual.

View File

@ -88,14 +88,14 @@ class MeleeAction(BaseAction):
#calculate message output
engine: Engine = self.entity.floor_map.engine
msg_text = f"{self.entity.name} attacked {target.name}"
msg: str = f"{self.entity.name} attacked {target.name}"
if damage > 0:
msg_text += f" for {damage} damage"
msg += f" for {damage} damage"
else:
msg_text += f" but was ineffective"
msg += f" but was ineffective"
engine.message_log.add_message(text = msg_text)
engine.message_log.add_message(msg)
#performing the actual change here, so the player's death event is at the bottom of the message log
target.stats.current_hp -= damage
@ -128,24 +128,26 @@ class PickupAction(BaseAction):
floor_map: FloorMap = self.entity.floor_map
engine: Engine = floor_map.engine
item_stack: List[Entity] = floor_map.get_all_entities_at(x, y, items_only=True)
item_pile: List[Entity] = floor_map.get_all_entities_at(x, y, items_only=True)
if len(item_stack) == 0:
if len(item_pile) == 0:
return False
elif len(item_stack) == 1:
msg = "you picked up a(n) {item_stack[0].name}"
if item_stack[0].useable.current_stack > 1:
msg = msg = f"you picked up a stack of {item_stack[0].useable.current_stack} {item_stack[0].name}"
elif len(item_pile) == 1:
item: Entity = item_pile.pop()
floor_map.entities.remove(item_stack[0])
self.entity.inventory.insert(item_stack[0])
msg: str = f"you picked up a(n) {item.name}"
if item.useable.current_stack > 1:
msg = f"you picked up a stack of {item.useable.current_stack} {item.name}"
floor_map.entities.remove(item)
self.entity.inventory.insert(item)
engine.message_log.add_message(msg, color=colors.terminal_light)
else:
from event_handlers import OptionSelector #circular imports are a pain
#build an options list
options: List[str] = []
for item in item_stack:
for item in item_pile:
options.append(item.name)
engine.event_handler = OptionSelector(
@ -153,16 +155,16 @@ class PickupAction(BaseAction):
parent_handler=engine.event_handler,
title="Pick Up Item",
options=options,
callback=lambda x: self.pickup_callback(engine, floor_map, self.entity, item_stack[x])
callback=lambda x: self.pickup_callback(engine, floor_map, self.entity, item_pile[x])
)
return True
#utils
def pickup_callback(self, engine: Engine, floor_map: FloorMap, entity: Entity, item: Entity) -> None:
msg = "you picked up a(n) {item.name}"
msg: str = f"you picked up a(n) {item.name}"
if item.useable.current_stack > 1:
msg = msg = f"you picked up a stack of {item.useable.current_stack} {item.name}"
msg = f"you picked up a stack of {item.useable.current_stack} {item.name}"
floor_map.entities.remove(item)
entity.inventory.insert(item)
@ -192,12 +194,17 @@ class DropAction(BaseAction):
item.x = x
item.y = y
#TODO: Check for floorpile stack merging
floor_map.entities.add(item)
if self.display_callback: #adjust the cursor
self.display_callback(-1)
engine.message_log.add_message(f"you dropped a(n) {item.name}", color=colors.terminal_light)
msg: str = f"you dropped a(n) {item.name}"
if item.useable.current_stack > 1:
msg = f"you dropped a stack of {item.useable.current_stack} {item.name}"
engine.message_log.add_message(msg, color=colors.terminal_light)
return True
@ -225,15 +232,18 @@ class DropPartialStackAction(BaseAction):
item: Entity = inventory.access(self.index)
#TODO: Check for floorpile stack merging
new_item: Entity = item.spawn(x, y, floor_map)
item.useable.current_stack -= self.amount
new_item.useable.current_stack = self.amount
if self.display_callback: #adjust the cursor
self.display_callback(-1)
self.display_callback(0) #by zero
engine.message_log.add_message(f"you dropped a stack of {new_item.useable.current_stack} {new_item.name}", color=colors.terminal_light)
msg: str = f"you dropped a partial stack of {new_item.useable.current_stack} {new_item.name}"
engine.message_log.add_message(msg, color=colors.terminal_light)
return True

View File

@ -1,14 +1,14 @@
#Standard colors
white = (0xFF, 0xFF, 0xFF)
black = (0x0, 0x0, 0x0)
white = (0xFF, 0xFF, 0xFF)
black = (0x0, 0x0, 0x0)
red = (0xFF, 0, 0)
green = (0, 0xFF, 0)
blue = (0, 0, 0xFF)
red = (0xFF, 0, 0)
green = (0, 0xFF, 0)
blue = (0, 0, 0xFF)
yellow = (0xFF, 0xFF, 0)
yellow = (0xFF, 0xFF, 0)
magenta = (0xFF, 0, 0xFF)
cyan = (0, 0xFF, 0xFF)
cyan = (0, 0xFF, 0xFF)
#gameboy DMG-01, according to Wikipedia's CSS
gameboy_00 = (0x29, 0x41, 0x39)
@ -18,4 +18,7 @@ gameboy_03 = (0x7b, 0x82, 0x10)
#terminal-like
terminal_light = (200, 200, 200)
terminal_dark = (100, 100, 100)
terminal_dark = (100, 100, 100)
#extended colors
orange = (0xFF, 0xA5, 0x00)

View File

@ -142,9 +142,17 @@ class GameplayHandler(EventHandler):
if key == tcod.event.KeySym.TAB:
self.engine.event_handler = InventoryViewer(self.engine, self, player)
#debugging
if key == tcod.event.KeySym.o: #TODO: remove this
self.engine.event_handler = OptionSelector(self.engine, self, options=["zero", "one", "two", "three"], callback=lambda x: print("You chose", x))
#debugging - can hook this up to more later
if (event.mod & tcod.event.Modifier.CTRL) and key == tcod.event.KeySym.d:
self.engine.event_handler = OptionSelector(
self.engine,
self,
title="Debug Selector",
options=["Zero", "One", "Two", "Three"],
callback=lambda x: self.engine.message_log.add_message(f"DBG: You selected {x}", colors.orange),
margin_x=20,
margin_y=12,
)
def ev_mousemotion(self, event: tcod.event.MouseMotion) -> None:
if self.engine.floor_map.in_bounds(event.tile.x, event.tile.y):
@ -255,10 +263,10 @@ class InventoryViewer(EventHandler):
string = msg,
fg=colors.terminal_light, bg=colors.black,
)
offset += 1
offset += 2
if self.length > 0:
inner_console.print(2, 2 + self.cursor, string = ">", fg=colors.terminal_light, bg=colors.black)
inner_console.print(2, 2 + self.cursor * 2, string = ">", fg=colors.terminal_light, bg=colors.black)
else:
#if inventory is empty, show a default message
inner_console.print_box(
@ -295,10 +303,14 @@ class InventoryViewer(EventHandler):
callback = lambda x: self.stack_selector_callback(x)
#TODO: drop 1, drop all for stacks
self.engine.event_handler = OptionSelector(self.engine, self,
self.engine.event_handler = OptionSelector(
self.engine,
self,
title=item.name,
options=options,
callback=callback
callback=callback,
margin_x=20,
margin_y=12,
)
#TODO: hotkeys via a config
@ -319,7 +331,7 @@ class InventoryViewer(EventHandler):
return self.drop()
elif selected == 2: #Back
return None #the selector does the change
def stack_selector_callback(self, selected: int) -> Optional[BaseAction]:
if selected == 0: #Use
return self.use()
@ -336,7 +348,7 @@ class InventoryViewer(EventHandler):
index = self.cursor
return UsageAction(self.entity, index, self.entity, lambda x: self.adjust_length(x))
def drop_partial_stack(self, amount: int) -> Optional[BaseAction]:
"""Drop part of an item stack at the cursor's position, and adjust the cursor if needed."""
if self.length > 0:
@ -350,7 +362,7 @@ class InventoryViewer(EventHandler):
index = self.cursor
return DropAction(self.entity, index, lambda x: self.adjust_length(x))
def adjust_length(self, amount: int):
self.length += amount
if self.cursor >= self.length:
@ -408,9 +420,9 @@ class OptionSelector(EventHandler):
string = option,
fg=colors.terminal_light, bg=colors.black,
)
offset += 1
offset += 2
select_console.print(2, 2 + self.cursor, string = ">", fg=colors.terminal_light, bg=colors.black)
select_console.print(2, 2 + self.cursor * 2, string = ">", fg=colors.terminal_light, bg=colors.black)
select_console.blit(console, self.margin_x, self.margin_y)

View File

@ -16,7 +16,7 @@ class Inventory:
return False
#check for stacking
if item.useable.maximum_stack > 0:
if item.useable.maximum_stack > 1:
if self.try_stack_merge(item):
return True
@ -34,7 +34,7 @@ class Inventory:
return None
else:
return self._contents.pop(index)
def discard(self, index: int) -> None:
if index < 0 or index >= len(self._contents):
pass
@ -44,15 +44,16 @@ class Inventory:
@property
def contents(self) -> List[Entity]:
return self._contents
#utils
def try_stack_merge(self, new_item: Entity):
for item in self._contents:
if item.useable.is_stack_mergable(new_item.useable):
#TODO: add a callback in the entity if other components need to be tweaked down the road
#NOTE: I'll add a callback in the entity if other components need to be tweaked down the road
item.useable.current_stack += new_item.useable.current_stack
new_item.useable.current_stack = 0 #just in case
return True
return False
#TODO: items need a weight, inventory needs a max capacity
#TODO: items need a weight?
#TODO: inventory needs a max capacity?

View File

@ -30,7 +30,7 @@ class BaseUseable:
`appearance` is what the item looks like, and can be substituted into the result.
"""
return None #default
#utils
def reduce_stack(self, amount: int = 1) -> bool:
"""
@ -45,7 +45,7 @@ class BaseUseable:
def is_stack_empty(self) -> bool:
return self.consumable and self.maximum_stack > 0 and self.current_stack <= 0
def is_stack_mergable(self, other: BaseUseable) -> bool:
"""
If this returns `True`, this instance can be merged with the other instance.
@ -78,7 +78,7 @@ class PotionOfHealing(BaseUseable):
self.__last_roll = roll_dice(4, 4)
stats.current_hp += self.__last_roll
return self.reduce_stack()
def get_used_msg(self, appearance: str) -> Optional[str]:
return f"You restored {self.__last_roll} health."
@ -88,7 +88,7 @@ class PotionOfHealing(BaseUseable):
# Extra Healing: 8d8 | 6d8 | 4d8. If the result is above MaxHP, MaxHP is incrased by 5 | 2 | 0.
# Full Healing: 400 | 400 | 400. If the result is above MaxHP, MaxHP is incrased by 8 | 4 | 0.
#TODO: move this into a different file
#TODO: move this into a different file, 'utils.py'
import random
def roll_dice(number: int, sides: int) -> int:
total: int = 0