state sync; the basic early game works
This commit is contained in:
		@@ -9,13 +9,13 @@ import webview
 | 
			
		||||
 | 
			
		||||
import flask
 | 
			
		||||
 | 
			
		||||
from pylocal import core, desktop, dev, items, tick
 | 
			
		||||
from pylocal import core, actions, desktop, dev, items, tick
 | 
			
		||||
 | 
			
		||||
core.desktop_mode = True
 | 
			
		||||
sig_exit = threading.Event()
 | 
			
		||||
 | 
			
		||||
argp = argparse.ArgumentParser("seagull")
 | 
			
		||||
argp.add_argument("-d", "--debug", action="store_true")
 | 
			
		||||
argp.add_argument("-d", "--debug", action="store_true", help="Launches the game in \"debug mode\".")
 | 
			
		||||
argo = argp.parse_args()
 | 
			
		||||
 | 
			
		||||
@core.app.route("/")
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@ import threading
 | 
			
		||||
import flask
 | 
			
		||||
from gevent.pywsgi import WSGIServer
 | 
			
		||||
 | 
			
		||||
from pylocal import core, dev, items, tick
 | 
			
		||||
from pylocal import core, actions, dev, items, tick
 | 
			
		||||
 | 
			
		||||
sig_exit = threading.Event()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										19
									
								
								app/pylocal/actions.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								app/pylocal/actions.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
import json
 | 
			
		||||
import random
 | 
			
		||||
 | 
			
		||||
import flask
 | 
			
		||||
 | 
			
		||||
from . import core
 | 
			
		||||
 | 
			
		||||
def dice_roll(min=0, max=100, modifiers=[]):
 | 
			
		||||
    result = random.randint(min, max)
 | 
			
		||||
    for _, mod in modifiers:
 | 
			
		||||
        result += mod
 | 
			
		||||
    
 | 
			
		||||
    return result
 | 
			
		||||
 | 
			
		||||
@core.app.route("/act/steal/<resource>", methods=["POST"])
 | 
			
		||||
def steal_resource(resource):
 | 
			
		||||
    return flask.Response(json.dumps({
 | 
			
		||||
        "success": (dice_roll() >= 50)
 | 
			
		||||
    }), status=200, content_type="application/json")
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
import os
 | 
			
		||||
import subprocess
 | 
			
		||||
import xml.etree.ElementTree as xmltree
 | 
			
		||||
 | 
			
		||||
@@ -7,13 +8,18 @@ valid_resources = [
 | 
			
		||||
    "food", "shinies", "psi" # early game
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
rant_env = os.environ.copy()
 | 
			
		||||
rant_env["RANT_MODULES_PATH"] = (core.path_appdir / "rant").as_posix()
 | 
			
		||||
 | 
			
		||||
def generate_item(resource, target):
 | 
			
		||||
    if core.desktop_mode:
 | 
			
		||||
        rant_path = core.path_appdir / "opt/rant/bin/rant"
 | 
			
		||||
    else:
 | 
			
		||||
        rant_path = "rant" # rely on OS PATH
 | 
			
		||||
    proc_rant = subprocess.run([rant_path, (core.path_appdir / f"rant/{resource}/{target}.rant").as_posix()], capture_output=True)
 | 
			
		||||
    return proc_rant.stdout.decode()
 | 
			
		||||
    proc_rant = subprocess.run([rant_path, (core.path_appdir / f"rant/{resource}/{target}.rant").as_posix()], env=rant_env, capture_output=True)
 | 
			
		||||
    if proc_rant.stderr:
 | 
			
		||||
        core.log.warning("rant is throwing up:\n" + proc_rant.stderr.decode())
 | 
			
		||||
    return proc_rant.stdout.decode().strip()
 | 
			
		||||
 | 
			
		||||
class TickItem(object):
 | 
			
		||||
    def __init__(self, resource, amount, target):
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								app/pylocal/jsonizer.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								app/pylocal/jsonizer.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
import json
 | 
			
		||||
 | 
			
		||||
from . import items
 | 
			
		||||
 | 
			
		||||
class JSONizer(json.JSONEncoder):
 | 
			
		||||
    def default(self, obj):
 | 
			
		||||
        if isinstance(obj, items.TickItem):
 | 
			
		||||
            return {"resource": obj.resource, "amount": obj.amount, "desc": obj.desc}
 | 
			
		||||
        else:
 | 
			
		||||
            # if no encoding here, fall back to stdlib
 | 
			
		||||
            return super().default(obj)
 | 
			
		||||
@@ -4,7 +4,7 @@ import subprocess
 | 
			
		||||
 | 
			
		||||
import flask
 | 
			
		||||
 | 
			
		||||
from . import core, items
 | 
			
		||||
from . import core, items, jsonizer
 | 
			
		||||
 | 
			
		||||
def generate_flavor_text():
 | 
			
		||||
    if core.desktop_mode:
 | 
			
		||||
@@ -47,10 +47,10 @@ def tick():
 | 
			
		||||
        case 10: # ENCHUMAN
 | 
			
		||||
            result["items"] = {
 | 
			
		||||
                # TODO: read ranges from XML rule files
 | 
			
		||||
                "food": [items.TickItem("food", random.uniform(0.0, 20.0), "humans")],
 | 
			
		||||
                "shinies": [items.TickItem("food", random.uniform(0.0, 20.0), "humans")]
 | 
			
		||||
                "food": [items.TickItem("food", round(random.uniform(0.0, 20.0), 2), "humans") for i in range(random.randint(0, 3))],
 | 
			
		||||
                "shinies": [items.TickItem("shinies", round(random.uniform(0.0, 20.0), 2), "humans") for i in range(random.randint(0, 3))]
 | 
			
		||||
            }
 | 
			
		||||
        case _:
 | 
			
		||||
            core.log.warning("undefined tick: {0}".format(result["event_type"]))
 | 
			
		||||
 | 
			
		||||
    return flask.Response(json.dumps(result), status=200, content_type="application/json")
 | 
			
		||||
    return flask.Response(json.dumps(result, cls=jsonizer.JSONizer), status=200, content_type="application/json")
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
@require "../wordlist"
 | 
			
		||||
@require "wordlist"
 | 
			
		||||
[$desc_food] @text {
 | 
			
		||||
    {
 | 
			
		||||
        [pick: <wordlist/adjectives/food>] @weight 1.25 |
 | 
			
		||||
@@ -31,6 +31,7 @@
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
[$mod_order] @text {
 | 
			
		||||
    {
 | 
			
		||||
        add | no | sub | extra |
 | 
			
		||||
@@ -43,11 +44,12 @@
 | 
			
		||||
        [pick: <wordlist/nouns/plants>]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
##
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    a piece of `[pick: <wordlist/nouns/cheese>] |
 | 
			
		||||
    a `{
 | 
			
		||||
        [if: [%maybe]]{[desc_food]} [get_entree] |
 | 
			
		||||
        [if: [maybe]{[desc_food]}] [get_entree] |
 | 
			
		||||
        [pick: <wordlist/nouns/fruit>]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<?xml version="1.0"?>
 | 
			
		||||
<ItemRules>
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<ItemRules xmlns="seagull:rules/items">
 | 
			
		||||
    <Event>ENCHUMAN</Event>
 | 
			
		||||
    <Food>
 | 
			
		||||
        <Min>0</Min>
 | 
			
		||||
@@ -9,15 +9,15 @@
 | 
			
		||||
        <Min>0</Min>
 | 
			
		||||
        <Max>20</Max>
 | 
			
		||||
    </Shinies>
 | 
			
		||||
    <Food StoryBeat=3>
 | 
			
		||||
    <Food StoryBeat="3">
 | 
			
		||||
        <Min>0</Min>
 | 
			
		||||
        <Max>20</Max>
 | 
			
		||||
    </Food>
 | 
			
		||||
    <Shinies StoryBeat=3>
 | 
			
		||||
    <Shinies StoryBeat="3">
 | 
			
		||||
        <Min>0</Min>
 | 
			
		||||
        <Max>50</Max>
 | 
			
		||||
    </Shinies>
 | 
			
		||||
    <Psi StoryBeat=3>
 | 
			
		||||
    <Psi StoryBeat="3">
 | 
			
		||||
        <Min>0</Min>
 | 
			
		||||
        <Max>15</Max>
 | 
			
		||||
    </Psi>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								app/rules/schemas/catalog.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								app/rules/schemas/catalog.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
 | 
			
		||||
  <uri name="seagull:rules/items" uri="items.xsd" />
 | 
			
		||||
</catalog>
 | 
			
		||||
							
								
								
									
										37
									
								
								app/rules/schemas/items.xsd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								app/rules/schemas/items.xsd
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="seagull:rules/items">
 | 
			
		||||
  <xs:element name="ItemRules">
 | 
			
		||||
    <xs:complexType>
 | 
			
		||||
      <xs:choice maxOccurs="unbounded" minOccurs="0">
 | 
			
		||||
        <xs:element type="xs:string" name="Event"/>
 | 
			
		||||
        <xs:element name="Food">
 | 
			
		||||
          <xs:complexType>
 | 
			
		||||
            <xs:sequence>
 | 
			
		||||
              <xs:element type="xs:int" name="Min"/>
 | 
			
		||||
              <xs:element type="xs:int" name="Max"/>
 | 
			
		||||
            </xs:sequence>
 | 
			
		||||
            <xs:attribute type="xs:int" name="StoryBeat" use="optional" default="0"/>
 | 
			
		||||
          </xs:complexType>
 | 
			
		||||
        </xs:element>
 | 
			
		||||
        <xs:element name="Shinies">
 | 
			
		||||
          <xs:complexType>
 | 
			
		||||
            <xs:sequence>
 | 
			
		||||
              <xs:element type="xs:int" name="Min"/>
 | 
			
		||||
              <xs:element type="xs:int" name="Max"/>
 | 
			
		||||
            </xs:sequence>
 | 
			
		||||
            <xs:attribute type="xs:int" name="StoryBeat" use="optional" default="0"/>
 | 
			
		||||
          </xs:complexType>
 | 
			
		||||
        </xs:element>
 | 
			
		||||
        <xs:element name="Psi">
 | 
			
		||||
          <xs:complexType>
 | 
			
		||||
            <xs:sequence>
 | 
			
		||||
              <xs:element type="xs:int" name="Min"/>
 | 
			
		||||
              <xs:element type="xs:int" name="Max"/>
 | 
			
		||||
            </xs:sequence>
 | 
			
		||||
            <xs:attribute type="xs:int" name="StoryBeat" default="0"/>
 | 
			
		||||
          </xs:complexType>
 | 
			
		||||
        </xs:element>
 | 
			
		||||
      </xs:choice>
 | 
			
		||||
    </xs:complexType>
 | 
			
		||||
  </xs:element>
 | 
			
		||||
</xs:schema>
 | 
			
		||||
		Reference in New Issue
	
	Block a user