├── alife
├── alife_camp.py
├── alife_work.py
├── alife_manage_items.py
├── stances.py
├── __init__.py
├── alife_follow.py
├── alife_cover.py
├── alife_escape.py
├── alife_guard.py
├── alife_group.py
├── alife_search.py
├── alife_manage_targets.py
├── snapshots.py
├── alife_hidden.py
├── alife_discover.py
├── alife_explore.py
├── alife_shelter.py
├── alife_surrender.py
├── alife_needs.py
├── raids.py
├── alife_combat.py
├── noise.py
└── memory.py
├── overwatch
├── __init__.py
└── events.py
├── data
├── text
│ ├── places.txt
│ └── human_first_names.txt
├── tiles
│ ├── soldier4_22lr.png
│ ├── soldier2_source.png
│ ├── soldier3_source.ase
│ ├── soldier4_glock17.png
│ ├── soldier4_mac10.png
│ ├── soldier4_unarmed.png
│ ├── terminal16x16_aa_inrow.png
│ ├── terminal8x8_gs_as_incol.png
│ ├── terminal16x16_gs_as_incol.png
│ └── terminal16x16_aa_as_incol_tiles.png
├── items
│ ├── burner.json
│ ├── radio.json
│ ├── office_chair.json
│ ├── desk.json
│ ├── gas_stove.json
│ ├── white_cloth.json
│ ├── gas_mask.json
│ ├── kevlar_helmet.json
│ ├── bed.json
│ ├── can_of_corn.json
│ ├── coat_rack.json
│ ├── cupboard.json
│ ├── military_crate.json
│ ├── molotov.json
│ ├── wooden_dresser.json
│ ├── sneakers.json
│ ├── metal_shelving.json
│ ├── 5.45x39mm_mag.json
│ ├── frag_grenade.json
│ ├── wooden_display_case.json
│ ├── m9.json
│ ├── 22_lr_mag.json
│ ├── aspirin.json
│ ├── cz_511.json
│ ├── scout_pack.json
│ ├── 22_rifle.json
│ ├── glock.json
│ ├── mp5_mag.json
│ ├── soda.json
│ ├── utility_backpack.json
│ ├── alice_pack.json
│ ├── chest_holster.json
│ ├── metal_door.json
│ ├── wooden_door.json
│ ├── 9x19mm_round.json
│ ├── leather_backpack.json
│ ├── mp5.json
│ ├── 5.45x39mm_round.json
│ ├── blue_jeans.json
│ ├── kevlar_jacket.json
│ ├── ak74.json
│ ├── electric_lantern.json
│ ├── fall_camo_pants.json
│ ├── 22_lr_cartridge.json
│ ├── 9x19mm_mag.json
│ ├── white_shirt.json
│ ├── blue_shirt.json
│ ├── trenchcoat.json
│ └── brown_hoodie.json
├── missions
│ ├── capture_territory.dat
│ ├── retrieve_item.dat
│ ├── travel_to.dat
│ ├── locate_target.dat
│ ├── kill_target.dat
│ ├── fetch_item.dat
│ ├── deliver_item.dat
│ └── zes_glock.dat
├── life
│ ├── night_terror.goap
│ ├── dog.goap
│ ├── dog.dat
│ ├── night_terror.dat
│ ├── human.dat
│ ├── night_terror.json
│ ├── night_terror.xml
│ ├── dog.json
│ ├── dog.xml
│ ├── human.goap
│ └── human.json
└── prefabs
│ └── test.json
├── art
└── pngs
│ ├── minilogo.png
│ └── flagsdev_logo.png
├── docs
├── weapons.md
├── missions.md
├── mission_planning.md
├── testingnotes.md
├── style_guide.md
├── alife.md
└── changelog.txt
├── freeze.py
├── tools
├── life_dump.py
├── show_profile.py
├── templates
│ ├── memory.html
│ ├── group.html
│ ├── camp.html
│ ├── index.html
│ └── life.html
├── ReactorWatch.py
└── guntest.py
├── render_map_setup.py
├── smp.py
├── .gitignore
├── compile_cython_modules.py
├── locks.py
├── tests
├── M01_data_structure.py
├── M01_data_structure_3d.py
├── M05_gas_mode.py
├── fast_render_simple_numpy.py
├── M07_recoil.py
├── M06_fov.py
├── M01_data_visualization.py
├── M06_Melee.py
└── M01_map_editor.py
├── license.txt
├── timers.py
├── debug.py
├── crafting.py
├── pyfov.py
├── threads.py
├── contexts.py
├── scripting.py
├── maputils.py
├── encounters.py
├── events.py
├── artifacts.py
├── profiles.py
├── render_fast_los.pyx
├── inputs.py
├── network.py
├── fast_scan_surroundings.pyx
├── readme.md
├── cache.py
├── prefabs.py
├── render_los.pyx
├── drawing.py
└── render_map.pyx
/alife/alife_camp.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/overwatch/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/data/text/places.txt:
--------------------------------------------------------------------------------
1 | Hope
2 | Genesis
3 | Fortitude
4 |
--------------------------------------------------------------------------------
/art/pngs/minilogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flags/Reactor-3/HEAD/art/pngs/minilogo.png
--------------------------------------------------------------------------------
/art/pngs/flagsdev_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flags/Reactor-3/HEAD/art/pngs/flagsdev_logo.png
--------------------------------------------------------------------------------
/data/tiles/soldier4_22lr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flags/Reactor-3/HEAD/data/tiles/soldier4_22lr.png
--------------------------------------------------------------------------------
/data/tiles/soldier2_source.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flags/Reactor-3/HEAD/data/tiles/soldier2_source.png
--------------------------------------------------------------------------------
/data/tiles/soldier3_source.ase:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flags/Reactor-3/HEAD/data/tiles/soldier3_source.ase
--------------------------------------------------------------------------------
/data/tiles/soldier4_glock17.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flags/Reactor-3/HEAD/data/tiles/soldier4_glock17.png
--------------------------------------------------------------------------------
/data/tiles/soldier4_mac10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flags/Reactor-3/HEAD/data/tiles/soldier4_mac10.png
--------------------------------------------------------------------------------
/data/tiles/soldier4_unarmed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flags/Reactor-3/HEAD/data/tiles/soldier4_unarmed.png
--------------------------------------------------------------------------------
/data/tiles/terminal16x16_aa_inrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flags/Reactor-3/HEAD/data/tiles/terminal16x16_aa_inrow.png
--------------------------------------------------------------------------------
/data/tiles/terminal8x8_gs_as_incol.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flags/Reactor-3/HEAD/data/tiles/terminal8x8_gs_as_incol.png
--------------------------------------------------------------------------------
/data/tiles/terminal16x16_gs_as_incol.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flags/Reactor-3/HEAD/data/tiles/terminal16x16_gs_as_incol.png
--------------------------------------------------------------------------------
/data/tiles/terminal16x16_aa_as_incol_tiles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flags/Reactor-3/HEAD/data/tiles/terminal16x16_aa_as_incol_tiles.png
--------------------------------------------------------------------------------
/docs/weapons.md:
--------------------------------------------------------------------------------
1 | WEP Range/ammo Feed
2 | -----------------------------------
3 | MP5 100m (9x19mm) 15, 30
4 | Glock 50m (9x19mm) 17, 19 (Floor plate)
5 |
6 |
--------------------------------------------------------------------------------
/docs/missions.md:
--------------------------------------------------------------------------------
1 | scout run: Gather intel on NPC/group location
2 | supply run: Travel with group to location, gather airdrop
3 | artifact hunt: Anomaly detected; investigate
4 |
--------------------------------------------------------------------------------
/freeze.py:
--------------------------------------------------------------------------------
1 | from distutils.core import setup
2 |
3 | import py2exe
4 |
5 | setup(windows=[{'script': 'reactor-3.py'}],
6 | options = {"py2exe": {"packages": ['alife']}})
--------------------------------------------------------------------------------
/alife/alife_work.py:
--------------------------------------------------------------------------------
1 | from globals import *
2 |
3 | import life as lfe
4 |
5 | import missions
6 |
7 |
8 | def tick(life):
9 | if life['missions']:
10 | missions.do_mission(life, life['missions'].keys()[0])
11 |
--------------------------------------------------------------------------------
/data/items/burner.json:
--------------------------------------------------------------------------------
1 | {"name": "burner",
2 | "prefix": "a",
3 | "type": "artifact",
4 | "icon": 145,
5 | "description": "It is warm to the touch.",
6 | "flags": "SMOKING|BURNING",
7 | "size": "1x1",
8 | "material": "rock"}
9 |
--------------------------------------------------------------------------------
/data/items/radio.json:
--------------------------------------------------------------------------------
1 | {"name": "radio",
2 | "prefix": "a",
3 | "type": "radio",
4 | "icon": "r",
5 | "description": "A radio used to communicate with others.",
6 | "flags": "",
7 | "size": "2x2",
8 | "material": "metal",
9 | "thickness": 2}
10 |
--------------------------------------------------------------------------------
/data/items/office_chair.json:
--------------------------------------------------------------------------------
1 | {"name": "office chair",
2 | "prefix": "a",
3 | "type": "storage",
4 | "icon": "D",
5 | "description": "A chair.",
6 | "flags": "",
7 | "size": "4x6",
8 | "thickness": 3,
9 | "color": [[175, 175, 175], [145, 145, 145]],
10 | "material": "metal"}
11 |
--------------------------------------------------------------------------------
/data/missions/capture_territory.dat:
--------------------------------------------------------------------------------
1 | =CREATE
2 | TASK 1 "Travel."
3 | TASK 1 "Capture."
4 |
5 | =1
6 | WAIT IS_IN_TERRITORY %TERRITORY_ID%
7 | FINISH 1
8 | JUMP 2
9 |
10 | =2
11 | EXEC CAPTURE_TERRITORY %TERRITORY_ID%
12 | JUMP 3
13 |
14 | =3
15 | FINISH 2
16 | COMPLETE
17 |
--------------------------------------------------------------------------------
/data/missions/retrieve_item.dat:
--------------------------------------------------------------------------------
1 | =1
2 | WAIT CAN_SEE_ITEM %ITEM_UID%
3 | SET STARTING_CHUNK GET_CHUNK_KEY
4 | JUMP 2
5 |
6 | =2
7 | EXEC PICK_UP_ITEM %ITEM_UID%
8 | JUMPIF 3 HAS_ITEM %ITEM_UID%
9 | LOOP
10 |
11 | =3
12 | EXEC TRAVEL_TO_CHUNK %STARTING_CHUNK%
13 | COMPLETE
14 |
--------------------------------------------------------------------------------
/data/items/desk.json:
--------------------------------------------------------------------------------
1 | {"name": "desk",
2 | "prefix": "a",
3 | "type": "storage",
4 | "icon": "-",
5 | "description": "A desk.",
6 | "flags": "CAN_BURN",
7 | "size": "12x12",
8 | "capacity": "2x2",
9 | "thickness": 12,
10 | "color": [[92, 51, 23], [72, 31, 3]],
11 | "material": "wood"}
12 |
--------------------------------------------------------------------------------
/data/items/gas_stove.json:
--------------------------------------------------------------------------------
1 | {"name": "gas stove",
2 | "prefix": "a",
3 | "type": "storage",
4 | "icon": "#",
5 | "description": "A gas stove.",
6 | "flags": "CAN_BURN",
7 | "size": "12x12",
8 | "thickness": 12,
9 | "color": [[110, 110, 110], [150, 150, 150]],
10 | "material": "metal"}
11 |
--------------------------------------------------------------------------------
/data/items/white_cloth.json:
--------------------------------------------------------------------------------
1 | {"name": "white cloth",
2 | "prefix": "a",
3 | "type": "fabric",
4 | "icon": "W",
5 | "description": "A white piece of cloth. Good for bandages.",
6 | "flags": "CANSTACK|CAN_BURN|CAN_SOAK",
7 | "size": "1x1",
8 | "thickness": 1,
9 | "material": "cloth"}
10 |
--------------------------------------------------------------------------------
/data/items/gas_mask.json:
--------------------------------------------------------------------------------
1 | {"name": "gas mask",
2 | "prefix": "a",
3 | "type": "clothing",
4 | "icon": "V",
5 | "color": [[0, 0, 0], null],
6 | "description": "A gas mask.",
7 | "attaches_to": "head",
8 | "flags": "CAN_WEAR",
9 | "size": "2x1",
10 | "thickness": 3,
11 | "material": "metal"}
12 |
--------------------------------------------------------------------------------
/data/missions/travel_to.dat:
--------------------------------------------------------------------------------
1 | =CREATE
2 | TASK 1 "Go to."
3 |
4 | =1
5 | JUMPIF 2 IS_PLAYER
6 | EXEC TRAVEL_TO_CHUNK %CHUNK_KEY%
7 | JUMPIF 3 IS_IN_CHUNK %CHUNK_KEY%
8 | LOOP
9 |
10 | =2
11 | WAIT IS_IN_CHUNK %CHUNK_KEY%
12 | FINISH 1
13 | JUMP 3
14 |
15 | =3
16 | FINISH 1
17 | COMPLETE
18 |
--------------------------------------------------------------------------------
/data/items/kevlar_helmet.json:
--------------------------------------------------------------------------------
1 | {"name": "kevlar helmet",
2 | "prefix": "a",
3 | "type": "clothing",
4 | "icon": "O",
5 | "description": "A bulletproof helmet.",
6 | "attaches_to": "head",
7 | "flags": "CAN_WEAR|CAN_BURN|CAN_SOAK",
8 | "size": "3x3",
9 | "thickness": 10,
10 | "material": "kevlar"}
11 |
--------------------------------------------------------------------------------
/data/items/bed.json:
--------------------------------------------------------------------------------
1 | {"name": "bed",
2 | "prefix": "a",
3 | "type": "door",
4 | "icon": "8",
5 | "description": "A large wooden dresser.",
6 | "flags": "CAN_BURN",
7 | "size": "12x12",
8 | "capacity": "12x12",
9 | "thickness": 3,
10 | "color": [[191, 171, 143], [158, 134, 100]],
11 | "material": "wood"}
12 |
--------------------------------------------------------------------------------
/data/items/can_of_corn.json:
--------------------------------------------------------------------------------
1 | {"name": "corn",
2 | "prefix": "a can of",
3 | "type": "food",
4 | "icon": "%",
5 | "description": "A can of corn.",
6 | "flags": "",
7 | "modifiers": {},
8 | "size": "1x1",
9 | "modifiers": [{"value": "hunger", "increase": 8, "effective_for": 1000}],
10 | "material": "metal"}
11 |
--------------------------------------------------------------------------------
/data/items/coat_rack.json:
--------------------------------------------------------------------------------
1 | {"name": "coat rack",
2 | "prefix": "a",
3 | "type": "storage",
4 | "icon": "{",
5 | "description": "A thin metal rod with a few hooks.",
6 | "flags": "",
7 | "size": "12x12",
8 | "capacity": "12x6",
9 | "thickness": 1,
10 | "color": [[94, 75, 47], null],
11 | "material": "metal"}
12 |
--------------------------------------------------------------------------------
/data/items/cupboard.json:
--------------------------------------------------------------------------------
1 | {"name": "cupboard",
2 | "prefix": "a",
3 | "type": "storage",
4 | "icon": "|",
5 | "description": "A wooden cupboard.",
6 | "flags": "CAN_BURN",
7 | "size": "8x2",
8 | "capacity": "8x2",
9 | "thickness": 2,
10 | "color": [[133, 120, 91], [154, 135, 107]],
11 | "material": "wood"}
12 |
--------------------------------------------------------------------------------
/data/items/military_crate.json:
--------------------------------------------------------------------------------
1 | {"name": "military crate",
2 | "prefix": "a",
3 | "type": "storage",
4 | "icon": "=",
5 | "description": "A large metal crate.",
6 | "flags": "",
7 | "size": "14x14",
8 | "capacity": "14x14",
9 | "thickness": 8,
10 | "color": [[0, 150, 0], [0, 170, 0]],
11 | "material": "metal"}
12 |
--------------------------------------------------------------------------------
/data/items/molotov.json:
--------------------------------------------------------------------------------
1 | {"name": "molotov",
2 | "prefix": "a",
3 | "type": "explosive",
4 | "icon": "*",
5 | "description": "Improvised incendiary device.",
6 | "speed": 1,
7 | "drag": 0.02,
8 | "flags": "",
9 | "radius": 8,
10 | "damage": {"force": 0, "fire": 6},
11 | "size": "1x1",
12 | "material": "glass"}
13 |
--------------------------------------------------------------------------------
/data/items/wooden_dresser.json:
--------------------------------------------------------------------------------
1 | {"name": "wooden dresser",
2 | "prefix": "a",
3 | "type": "storage",
4 | "icon": "D",
5 | "description": "A wooden door.",
6 | "flags": "CAN_BURN",
7 | "size": "12x12",
8 | "capacity": "12x12",
9 | "thickness": 3,
10 | "color": [[94, 75, 47], [63, 50, 31]],
11 | "material": "wood"}
12 |
--------------------------------------------------------------------------------
/tools/life_dump.py:
--------------------------------------------------------------------------------
1 | #Life Dump
2 |
3 | import json
4 | import sys
5 |
6 | def dump(filename):
7 | with open(filename, 'r') as e:
8 | _life = json.loads(''.join(e.readlines()))
9 |
10 | print json.dumps(_life, indent=3)
11 |
12 | if __name__ == '__main__':
13 | if len(sys.argv)>1:
14 | dump(sys.argv[1])
--------------------------------------------------------------------------------
/data/items/sneakers.json:
--------------------------------------------------------------------------------
1 | {"name": "sneakers",
2 | "prefix": "a pair of",
3 | "type": "clothing",
4 | "icon": "s",
5 | "description": "A pair of running shoes.",
6 | "mods": {"speed": 0.5},
7 | "attaches_to": "lfoot|rfoot",
8 | "flags": "CAN_WEAR|CAN_BURN|CAN_SOAK",
9 | "size": "1x1",
10 | "thickness": 1,
11 | "material": "cloth"}
12 |
--------------------------------------------------------------------------------
/render_map_setup.py:
--------------------------------------------------------------------------------
1 | from distutils.core import setup
2 | from distutils.extension import Extension
3 | from Cython.Distutils import build_ext
4 |
5 | ext_modules = [Extension("render_map", ["render_map.pyx"])]
6 |
7 | setup(
8 | name = 'Render Map',
9 | cmdclass = {'build_ext': build_ext},
10 | ext_modules = ext_modules
11 | )
--------------------------------------------------------------------------------
/data/items/metal_shelving.json:
--------------------------------------------------------------------------------
1 | {"name": "metal shelving",
2 | "prefix": "the",
3 | "type": "storage",
4 | "icon": "=",
5 | "description": "A metal structure with several shelves.",
6 | "flags": "",
7 | "size": "3x3",
8 | "capacity": "3x3",
9 | "thickness": 3,
10 | "color": [[180, 180, 180], [130, 130, 130]],
11 | "material": "metal"}
12 |
--------------------------------------------------------------------------------
/data/items/5.45x39mm_mag.json:
--------------------------------------------------------------------------------
1 | {"name": "5.45x39mm magazine",
2 | "prefix": "a",
3 | "type": "magazine",
4 | "icon": "L",
5 | "description": "A magazine designed for use in the AK-74.",
6 | "flags": "",
7 | "size": "1x3",
8 | "maxrounds": 30,
9 | "rounds": [],
10 | "ammotype": "5.45x39mm",
11 | "material": "metal",
12 | "thickness": 3
13 | }
14 |
--------------------------------------------------------------------------------
/data/items/frag_grenade.json:
--------------------------------------------------------------------------------
1 | {"name": "frag grenade",
2 | "prefix": "a",
3 | "type": "explosive",
4 | "icon": "*",
5 | "description": "Mid-range frag grenade with a decent damage radius.",
6 | "speed": 1,
7 | "drag": 0.02,
8 | "flags": "",
9 | "radius": 8,
10 | "damage": {"force": 4, "fire": 3},
11 | "size": "1x1",
12 | "material": "metal"}
13 |
--------------------------------------------------------------------------------
/data/items/wooden_display_case.json:
--------------------------------------------------------------------------------
1 | {"name": "wooden display case",
2 | "prefix": "a",
3 | "type": "storage",
4 | "icon": "#",
5 | "description": "A metal structure with several shelves.",
6 | "flags": "",
7 | "size": "6x10",
8 | "capacity": "6x10",
9 | "thickness": 3,
10 | "color": [[161, 219, 236], [94, 75, 47]],
11 | "material": "wood"}
12 |
--------------------------------------------------------------------------------
/data/items/m9.json:
--------------------------------------------------------------------------------
1 | {"name": "M9",
2 | "prefix": "an",
3 | "type": "gun",
4 | "icon": "P",
5 | "description": "A pistol",
6 | "flags": "",
7 | "size": "4x2",
8 | "feed": "magazine",
9 | "ammotype": "9x19mm",
10 | "recoil": 0.4,
11 | "accuracy": 0.69,
12 | "firemodes": ["single"],
13 | "firemode": 0,
14 | "material": "metal",
15 | "thickness": 3}
16 |
--------------------------------------------------------------------------------
/data/missions/locate_target.dat:
--------------------------------------------------------------------------------
1 | =CREATE
2 | TASK 1 "Find target."
3 |
4 | =1
5 | EXEC TRACK_TARGET %TARGET%
6 | JUMPIF 2 IS_PLAYER
7 | EXEC SEARCH_FOR_TARGET %TARGET%
8 | JUMPIF 3 CAN_SEE_TARGET %TARGET%
9 | LOOP
10 |
11 | =2
12 | WAIT CAN_SEE_TARGET %TARGET%
13 | FINISH 1
14 | JUMP 3
15 |
16 | =3
17 | EXEC UNTRACK_TARGET %TARGET%
18 | COMPLETE
19 |
--------------------------------------------------------------------------------
/data/items/22_lr_mag.json:
--------------------------------------------------------------------------------
1 | {"name": ".22 LR magazine",
2 | "prefix": "a",
3 | "type": "magazine",
4 | "icon": "L",
5 | "description": "A magazine for use in .22 long rifles.",
6 | "flags": "",
7 | "size": "1x3",
8 | "maxrounds": 12,
9 | "rounds": [],
10 | "ammotype": ".22 LR",
11 | "material": "metal",
12 | "thickness": 3,
13 | "upgrades":
14 | {}
15 | }
16 |
--------------------------------------------------------------------------------
/data/items/aspirin.json:
--------------------------------------------------------------------------------
1 | {"name": "aspirin",
2 | "prefix": "an",
3 | "type": "medicine",
4 | "icon": "8",
5 | "description": "Pain medication. Recommended dose: 2 capsules.",
6 | "flags": "",
7 | "sustenance": 20,
8 | "value": 175,
9 | "modifiers": [{"value": "pain_threshold", "add": 0.2, "effective_for": 3000}],
10 | "size": "1x1",
11 | "material": "metal"}
12 |
--------------------------------------------------------------------------------
/data/items/cz_511.json:
--------------------------------------------------------------------------------
1 | {"name": "CZ 511",
2 | "prefix": "a",
3 | "type": "gun",
4 | "icon": "/",
5 | "description": ".22 rifle",
6 | "flags": "",
7 | "size": "18x1",
8 | "feed": "magazine",
9 | "ammotype": ".22 LR",
10 | "recoil": 0.5,
11 | "accuracy": 0.91,
12 | "firemodes": ["single"],
13 | "firemode": 0,
14 | "material": "metal",
15 | "thickness": 3}
16 |
--------------------------------------------------------------------------------
/data/items/scout_pack.json:
--------------------------------------------------------------------------------
1 | {"name": "scout pack",
2 | "prefix": "a",
3 | "type": "backpack",
4 | "icon": "b",
5 | "color": [[94, 75, 47], null],
6 | "description": "A small, low-profile backpack.",
7 | "attaches_to": "chest",
8 | "flags": "CAN_WEAR|CAN_BURN|CAN_SOAK",
9 | "size": "2x3",
10 | "capacity": "2x3",
11 | "thickness": 2,
12 | "material": "cloth"}
13 |
--------------------------------------------------------------------------------
/data/items/22_rifle.json:
--------------------------------------------------------------------------------
1 | {"name": ".22 rifle",
2 | "prefix": "a",
3 | "type": "gun",
4 | "icon": "/",
5 | "description": "A .22 rifle",
6 | "flags": "",
7 | "size": "19x1",
8 | "feed": "magazine",
9 | "ammotype": ".22 LR",
10 | "recoil": 0.55,
11 | "accuracy": 0.88,
12 | "firemodes": ["single"],
13 | "firemode": 0,
14 | "material": "metal",
15 | "thickness": 3}
16 |
--------------------------------------------------------------------------------
/data/items/glock.json:
--------------------------------------------------------------------------------
1 | {"name": "glock",
2 | "prefix": "a",
3 | "type": "gun",
4 | "icon": "p",
5 | "description": "A pistol",
6 | "flags": "",
7 | "size": "3x2",
8 | "feed": "magazine",
9 | "ammotype": "9x19mm",
10 | "recoil": 0.42,
11 | "accuracy": 0.6,
12 | "firemodes": ["single", "3burst"],
13 | "firemode": 0,
14 | "material": "metal",
15 | "thickness": 3}
16 |
--------------------------------------------------------------------------------
/data/items/mp5_mag.json:
--------------------------------------------------------------------------------
1 | {"name": "mp5 magazine",
2 | "prefix": "a",
3 | "type": "magazine",
4 | "icon": "[",
5 | "description": "A magazine for use in the MP5. Holds 15 rounds.",
6 | "flags": "",
7 | "size": "1x2",
8 | "maxrounds": 15,
9 | "rounds": [],
10 | "ammotype": "9x19mm",
11 | "material": "metal",
12 | "thickness": 4,
13 | "upgrades":
14 | {}
15 | }
16 |
--------------------------------------------------------------------------------
/data/items/soda.json:
--------------------------------------------------------------------------------
1 | {"name": "soda",
2 | "prefix": "a can of",
3 | "type": "drink",
4 | "icon": "8",
5 | "description": "A can of sugary soda. Contains caffeine.",
6 | "flags": "",
7 | "modifiers": [{"value": "thirst", "increase": 5, "effective_for": 1000}, {"value": "hunger", "increase": -1, "effective_for": 1000}],
8 | "size": "1x1",
9 | "material": "metal"}
10 |
--------------------------------------------------------------------------------
/data/items/utility_backpack.json:
--------------------------------------------------------------------------------
1 | {"name": "utility backpack",
2 | "prefix": "a",
3 | "type": "backpack",
4 | "icon": "B",
5 | "color": [[94, 75, 47], null],
6 | "description": "Medium-sized backpack.",
7 | "attaches_to": "chest",
8 | "flags": "CAN_WEAR|CAN_BURN|CAN_SOAK",
9 | "size": "6x7",
10 | "capacity": "6x7",
11 | "thickness": 5,
12 | "material": "cloth"}
13 |
--------------------------------------------------------------------------------
/smp.py:
--------------------------------------------------------------------------------
1 | from globals import *
2 |
3 | import life as lfe
4 | import alife
5 | import zones
6 | import fov as _fov
7 |
8 | import time
9 |
10 | def init():
11 | import pp
12 |
13 | SETTINGS['smp'] = pp.Server()
14 |
15 | def test(life, key=None):
16 | return (life, key)
17 |
18 | def process(callback, life, **kwargs):
19 | return [life, callback(life, **kwargs)]
20 |
--------------------------------------------------------------------------------
/data/items/alice_pack.json:
--------------------------------------------------------------------------------
1 | {"name": "ALICE pack",
2 | "prefix": "an",
3 | "type": "backpack",
4 | "icon": "B",
5 | "color": [[94, 75, 47], null],
6 | "description": "All-purpose Lightweight Individual Carrying Equipment.",
7 | "attaches_to": "chest",
8 | "flags": "CAN_WEAR|CAN_BURN|CAN_SOAK",
9 | "size": "7x7",
10 | "capacity": "7x7",
11 | "thickness": 3,
12 | "material": "cloth"}
13 |
--------------------------------------------------------------------------------
/data/items/chest_holster.json:
--------------------------------------------------------------------------------
1 | {"name": "chest holster",
2 | "prefix": "a",
3 | "type": "clothing",
4 | "icon": "/",
5 | "color": [[54, 75, 47], null],
6 | "description": "A tactical hoster that is worn around the chest.",
7 | "attaches_to": "chest",
8 | "flags": "CAN_WEAR|CAN_BURN|CAN_SOAK",
9 | "size": "2x2",
10 | "capacity": "2x2",
11 | "thickness": 3,
12 | "material": "cloth"}
13 |
--------------------------------------------------------------------------------
/data/items/metal_door.json:
--------------------------------------------------------------------------------
1 | {"name": "metal door",
2 | "prefix": "a",
3 | "type": "door",
4 | "icon": "+",
5 | "description": "A large metal door.",
6 | "flags": "CAN_BURN|CAN_BLOCK|ON_ACTIVATE[TOGGLE_BLOCK()]|ON_DEACTIVATE[TOGGLE_BLOCK()]|ON_COLLIDE[TOGGLE_BLOCK]",
7 | "size": "12x12",
8 | "thickness": 3,
9 | "color": [[120, 120, 120], [140, 140, 140]],
10 | "material": "metal"}
11 |
--------------------------------------------------------------------------------
/data/items/wooden_door.json:
--------------------------------------------------------------------------------
1 | {"name": "wooden door",
2 | "prefix": "a",
3 | "type": "door",
4 | "icon": "+",
5 | "description": "A large wooden dresser.",
6 | "flags": "CAN_BURN|CAN_BLOCK|ON_ACTIVATE[TOGGLE_BLOCK()]|ON_DEACTIVATE[TOGGLE_BLOCK()]|ON_COLLIDE[TOGGLE_BLOCK]",
7 | "size": "12x12",
8 | "thickness": 3,
9 | "color": [[127, 101, 63], [94, 75, 47]],
10 | "material": "wood"}
11 |
--------------------------------------------------------------------------------
/alife/alife_manage_items.py:
--------------------------------------------------------------------------------
1 | from globals import *
2 |
3 | import life as lfe
4 |
5 | import judgement
6 | import survival
7 |
8 | import logging
9 |
10 |
11 | def conditions(life):
12 | RETURN_VALUE = STATE_UNCHANGED
13 |
14 | if lfe.execute_raw(life, 'state', 'managing'):
15 | return True
16 |
17 | return False
18 |
19 | def tick(life):
20 | return survival.manage_inventory(life)
21 |
--------------------------------------------------------------------------------
/data/items/9x19mm_round.json:
--------------------------------------------------------------------------------
1 | {"name": "9x19mm round",
2 | "prefix": "a",
3 | "type": "bullet",
4 | "icon": ".",
5 | "description": "Pistol round effective at short to mid-range combat.",
6 | "speed": 38,
7 | "recoil": 5,
8 | "drag": 0.05,
9 | "scatter_rate": 0.3,
10 | "flags": "ON_STOP[DELETE()]",
11 | "damage": {"sharp": 13},
12 | "size": "1x1",
13 | "ammotype": "9x19mm",
14 | "material": "metal"}
15 |
--------------------------------------------------------------------------------
/data/items/leather_backpack.json:
--------------------------------------------------------------------------------
1 | {"name": "leather backpack",
2 | "prefix": "a",
3 | "type": "backpack",
4 | "icon": "B",
5 | "color": [[94, 75, 47], null],
6 | "description": "As the name suggests, it is a backpack made of quality leather.",
7 | "attaches_to": "chest",
8 | "flags": "CAN_WEAR|CAN_BURN|CAN_SOAK",
9 | "size": "5x5",
10 | "capacity": "5x5",
11 | "thickness": 3,
12 | "material": "cloth"}
13 |
--------------------------------------------------------------------------------
/data/items/mp5.json:
--------------------------------------------------------------------------------
1 | {"name": "mp5",
2 | "prefix": "an",
3 | "type": "gun",
4 | "icon": "m",
5 | "description": "A submachine gun. Effective at mid-range.",
6 | "flags": "",
7 | "size": "7x2",
8 | "feed": "magazine",
9 | "ammotype": "9x19mm",
10 | "recoil": 0.25,
11 | "accuracy": 0.63,
12 | "firemodes": ["single", "3burst", "auto"],
13 | "firemode": 1,
14 | "material": "metal",
15 | "thickness": 3}
16 |
--------------------------------------------------------------------------------
/data/items/5.45x39mm_round.json:
--------------------------------------------------------------------------------
1 | {"name": "5.45x39mm round",
2 | "prefix": "a",
3 | "type": "bullet",
4 | "icon": ".",
5 | "description": "Rifle round effective at mid to long-range combat.",
6 | "speed": 45,
7 | "recoil": 3,
8 | "drag": 0.005,
9 | "scatter_rate": 0.2,
10 | "flags": "ON_STOP[DELETE()]",
11 | "damage": {"sharp": 19},
12 | "size": "1x1",
13 | "ammotype": "5.45x39mm",
14 | "material": "metal"}
15 |
--------------------------------------------------------------------------------
/data/items/blue_jeans.json:
--------------------------------------------------------------------------------
1 | {"name": "blue jeans",
2 | "prefix": "a pair of",
3 | "type": "clothing",
4 | "icon": "H",
5 | "color": [[0, 0, 255], null],
6 | "description": "A sturdy pair of blue jeans.",
7 | "attaches_to": "hip|rthigh|lthigh|lknee|rknee|llowerleg|rlowerleg",
8 | "flags": "CAN_WEAR|CAN_BURN|CAN_SOAK",
9 | "size": "1x1",
10 | "capacity": "2x1",
11 | "thickness": 1,
12 | "material": "cloth"}
13 |
--------------------------------------------------------------------------------
/data/items/kevlar_jacket.json:
--------------------------------------------------------------------------------
1 | {"name": "kevlar jacket",
2 | "prefix": "a",
3 | "type": "clothing",
4 | "icon": "T",
5 | "description": "A bulletproof jacket.",
6 | "attaches_to": "chest|neck|stomach|lshoulder|rshoulder|lupperarm|rupperarm|lelbow|relbow|lforearm|rforearm",
7 | "flags": "CAN_WEAR|CAN_BURN|CAN_SOAK",
8 | "size": "6x6",
9 | "capacity": "2x2",
10 | "thickness": 12,
11 | "material": "kevlar"}
12 |
--------------------------------------------------------------------------------
/tools/show_profile.py:
--------------------------------------------------------------------------------
1 | #Reads profile, prints sorted output
2 | import pstats
3 | import sys
4 |
5 | if len(sys.argv)>=2 and sys.argv[1].count('.dat'):
6 | profile = sys.argv[1]
7 | else:
8 | profile = 'profile.dat'
9 |
10 | stats = pstats.Stats(profile)
11 |
12 | if 'highest' in sys.argv:
13 | stats.sort_stats('cumulative').print_stats(20)
14 | else:
15 | stats.strip_dirs().sort_stats(-1).print_stats()
16 |
--------------------------------------------------------------------------------
/data/items/ak74.json:
--------------------------------------------------------------------------------
1 | {"name": "AK-74",
2 | "prefix": "an",
3 | "type": "gun",
4 | "icon": "m",
5 | "description": "Kalashnikov automatic. Good for long-range engagements.",
6 | "flags": "",
7 | "size": "9x2",
8 | "feed": "magazine",
9 | "ammotype": "5.45x39mm",
10 | "recoil": 0.35,
11 | "accuracy": 0.72,
12 | "firemodes": ["single", "3burst", "auto"],
13 | "firemode": 1,
14 | "material": "metal",
15 | "thickness": 3}
16 |
--------------------------------------------------------------------------------
/data/items/electric_lantern.json:
--------------------------------------------------------------------------------
1 | {"name": "electric lantern",
2 | "prefix": "an",
3 | "type": "light",
4 | "icon": 15,
5 | "description": "A small light. Built with metal.",
6 | "brightness": 2,
7 | "light_shake": 0.3,
8 | "flags": "ON_ACTIVATE[LIGHT_FOLLOW(self, self.brightness, self.light_shake)]|ON_DEACTIVATE[LIGHT_FOLLOW_REMOVE(self, self.brightness, self.light_shake)]",
9 | "size": "2x2",
10 | "material": "metal"}
11 |
--------------------------------------------------------------------------------
/data/missions/kill_target.dat:
--------------------------------------------------------------------------------
1 | =CREATE
2 | TASK 1 "Find target."
3 | TASK 2 "Kill target."
4 |
5 | =1
6 | JUMPIF 2 IS_PLAYER
7 | EXEC SEARCH_FOR_TARGET %TARGET%
8 | JUMPIF 3 CAN_SEE_TARGET %TARGET%
9 | LOOP
10 |
11 | =2
12 | EXEC TRACK_TARGET %TARGET%
13 | WAIT CAN_SEE_TARGET %TARGET%
14 | FINISH 1
15 | JUMP 4
16 |
17 | =3
18 | FINISH 1
19 | JUMP 4
20 |
21 | =4
22 | WAIT IS_TARGET_DEAD %TARGET%
23 | FINISH 2
24 | COMPLETE
25 |
--------------------------------------------------------------------------------
/alife/stances.py:
--------------------------------------------------------------------------------
1 | import brain
2 |
3 | import judgement
4 |
5 | import logging
6 |
7 | def get_stance_towards(life, target_id):
8 | _know = brain.knows_alife_by_id(life, target_id)
9 |
10 | if _know:
11 | if judgement.can_trust(life, target_id):
12 | return 'friendly'
13 | elif judgement.is_target_dangerous(life, target_id):
14 | return 'hostile'
15 | else:
16 | return 'neutral'
17 | else:
18 | return 'neutral'
--------------------------------------------------------------------------------
/data/items/fall_camo_pants.json:
--------------------------------------------------------------------------------
1 | {"name": "fall camo pants",
2 | "prefix": "a pair of",
3 | "type": "clothing",
4 | "icon": "H",
5 | "color": [[94, 75, 47], null],
6 | "description": "Pants with fall camo. Good for blending in.",
7 | "attaches_to": "hip|rthigh|lthigh|lknee|rknee|llowerleg|rlowerleg",
8 | "flags": "CAN_WEAR|CAN_BURN|CAN_SOAK",
9 | "size": "1x1",
10 | "capacity": "2x2",
11 | "thickness": 1,
12 | "material": "cloth"}
13 |
--------------------------------------------------------------------------------
/data/items/22_lr_cartridge.json:
--------------------------------------------------------------------------------
1 | {"name": ".22 LR cartridge",
2 | "prefix": "a",
3 | "type": "bullet",
4 | "icon": ".",
5 | "color": [[94, 75, 47], null],
6 | "description": "Long Rifle cartridge effective at mid to long range.",
7 | "speed": 35,
8 | "recoil": 3,
9 | "drag": 0.05,
10 | "scatter_rate": 0.1,
11 | "flags": "ON_STOP[DELETE()]",
12 | "damage": {"sharp": 17},
13 | "size": "1x1",
14 | "ammotype": ".22 LR",
15 | "material": "metal"}
16 |
--------------------------------------------------------------------------------
/alife/__init__.py:
--------------------------------------------------------------------------------
1 | ############
2 | # ALife v2 ########################
3 | # Created by Luke Martin (flags) #
4 | ###################################
5 | # Started: 12:10 AM, 1/16/2013 #
6 | # Ended: Probably not for a while #
7 | ###################################
8 |
9 | __all__ = ['action', 'snapshots', 'judgement', 'chunks', 'brain', 'speech', 'stances', 'jobs', 'groups', 'factions', 'camps', 'sight', 'rawparse', 'noise', 'planner']
10 |
--------------------------------------------------------------------------------
/alife/alife_follow.py:
--------------------------------------------------------------------------------
1 | from globals import *
2 |
3 | import life as lfe
4 |
5 | import judgement
6 | import movement
7 | import sight
8 |
9 |
10 | def tick(life):
11 | _guard = judgement.get_target_to_guard(life)
12 |
13 | if _guard:
14 | return movement.find_target(life, _guard, follow=False, distance=sight.get_vision(life)*.25, call=False)
15 |
16 | return movement.find_target(life, judgement.get_target_to_follow(life), follow=True, call=False)
--------------------------------------------------------------------------------
/data/items/9x19mm_mag.json:
--------------------------------------------------------------------------------
1 | {"name": "9x19mm magazine",
2 | "prefix": "a",
3 | "type": "magazine",
4 | "icon": "L",
5 | "description": "A magazine designed for use in the Glock 17. Extendable to 19 rounds with optional floor plate.",
6 | "flags": "",
7 | "size": "1x2",
8 | "maxrounds": 17,
9 | "rounds": [],
10 | "ammotype": "9x19mm",
11 | "material": "metal",
12 | "thickness": 3,
13 | "upgrades":
14 | {"ext. 9x19mm mag floor plate":
15 | {"magsize": 19}
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/data/missions/fetch_item.dat:
--------------------------------------------------------------------------------
1 | =1
2 | SET LOCATION_CHUNK FIND_NEAREST_CHUNK_IN_REFERENCE TOWNS
3 | JUMP 2
4 |
5 | =2
6 | EXEC TRAVEL_TO_CHUNK %LOCATION_CHUNK%
7 | WAIT IS_IN_CHUNK %LOCATION_CHUNK%
8 | #WAIT SEARCH_FOR_ITEM %ITEM%
9 |
10 | JUMPIF 3 HAS_ITEM_TYPE %ITEM%
11 | JUMP 2
12 |
13 | =3
14 | JUMPIF 3 HAS_FACTION_BASE
15 | JUMPIF 4 %GATHERED_ITEM%&!HAS_FACTION_BASE
16 |
17 | =4
18 | SET LOCATION_CHUNK FIND_FACTION_BASE
19 | EXEC TRAVEL_TO_CHUNK %LOCATION_CHUNK%
20 | COMPLETE
21 |
22 | =5
23 | COMPLETE
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyd
2 | *.pyc
3 | *.c
4 | *.so
5 | *.sh
6 | *.xcf
7 | *.swp
8 | *.wpr
9 | *.gif
10 | *.png
11 | *.ase
12 | *.txt
13 | *.patch
14 | *.lprof
15 | *.idea_wall
16 | *.html
17 | *.prof
18 | *.zip
19 | *_simple_*
20 | *.dll
21 | *.wpu
22 |
23 | .stats/*
24 | .idea/*
25 | art/txt/*
26 | build/*
27 | tests/*
28 | dist/*
29 | data/maps*
30 | docs/devjournal.md
31 | docs/shortlist.md
32 | docs/talks/*
33 |
34 | mergedown
35 | nounlist.py
36 | kernprof.py
37 | profile.dat
38 | profile.txt
39 | diffout.txt
40 | data/text/wikiscrape.py
41 |
--------------------------------------------------------------------------------
/data/text/human_first_names.txt:
--------------------------------------------------------------------------------
1 | Abram
2 | Alexander
3 | Anatoly
4 | Artyom
5 | Bogdan
6 | Vadim
7 | Veniamin
8 | Vladislav
9 | Vyacheslav
10 | Gavriil
11 | Georgy
12 | Daniil
13 | Evgeny
14 | Yegor
15 | Yefim
16 | Zakhar
17 | Ignat
18 | Illarion
19 | Immanuil
20 | Iosif
21 | Lev
22 | Leonid
23 | Makar
24 | Marat
25 | Matvei
26 | Mikhail
27 | Nikolay
28 | Oleg
29 | Pavel
30 | Pyotr
31 | Rodion
32 | Rostislav
33 | Ruslan
34 | Semyon
35 | Sergei
36 | Spartak
37 | Stanislav
38 | Stepan
39 | Timofei
40 | Timur
41 | Trofim
42 | Eduard
43 | Yulian
44 | Yakov
45 | Yaroslav
--------------------------------------------------------------------------------
/compile_cython_modules.py:
--------------------------------------------------------------------------------
1 | from distutils.core import setup
2 | from distutils.extension import Extension
3 | from Cython.Distutils import build_ext
4 |
5 | setup(
6 | cmdclass = {'build_ext': build_ext},
7 | ext_modules = [Extension('render_map', ['render_map.pyx']),
8 | Extension('render_los', ['render_los.pyx']),
9 | Extension('render_fast_los', ['render_fast_los.pyx']),
10 | Extension('fast_dijkstra', ['fast_dijkstra.pyx']),
11 | Extension('fast_scan_surroundings', ['fast_scan_surroundings.pyx']),
12 | Extension('fov', ['fov.pyx'])]
13 | )
14 |
--------------------------------------------------------------------------------
/data/missions/deliver_item.dat:
--------------------------------------------------------------------------------
1 | =CREATE
2 | TASK 1 "Find item."
3 | TASK 2 "Deliver item."
4 |
5 | =1
6 | WAIT HAS_ITEM %ITEM_UID%
7 | FINISH 1
8 | EXEC TRACK_TARGET %TARGET%
9 | JUMPIF 2 IS_PLAYER
10 | JUMP 3
11 |
12 | =2
13 | WAIT CAN_SEE_TARGET %TARGET%
14 | EXEC UNTRACK_TARGET %TARGET%
15 | WAIT !HAS_ITEM %ITEM_UID%
16 | FINISH 2
17 | JUMP 5
18 |
19 | =3
20 | EXEC SEARCH_FOR_TARGET %TARGET%
21 | JUMPIF 4 CAN_SEE_TARGET %TARGET%
22 | LOOP
23 |
24 | =4
25 | EXEC DROP_ITEM %ITEM_UID%
26 | WAIT !HAS_ITEM %ITEM_UID%
27 | FINISH 2
28 | JUMP 5
29 |
30 | =5
31 | COMPLETE
32 |
--------------------------------------------------------------------------------
/tools/templates/memory.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | ReactorWatch - Memory {{ life_id }}
4 |
5 |
6 | Life #{{ life_id }}
7 |
8 | - Memories ({{ memories|count }}):
9 | {% for entry in memories %}
10 | {{ entry.text }}
11 |
12 | {% for key in entry %}
13 | - {{ key }}: {{ entry[key] }}
14 | {% endfor %}
15 |
16 | {% endfor %}
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/data/life/night_terror.goap:
--------------------------------------------------------------------------------
1 | [GOAL_DISCOVER]
2 | DESIRE: HAS_NON_RELAXED_GOAL
3 | TIER: TIER_RELAXED
4 |
5 | [GOAL_LOOT]
6 | DESIRE: !HAS_NEEDS
7 | TIER: TIER_SURVIVAL
8 | SET_FLAGS: {"wanted_items": GET_NEEDED_ITEMS}
9 |
10 | [ACTION_WANDER]
11 | SATISFIES: HAS_NON_RELAXED_GOAL
12 | LOOP_UNTIL: NEVER
13 | EXECUTE: WANDER
14 |
15 | [ACTION_PICK_UP_ITEM]
16 | SATISFIES: HAS_NEEDS
17 | DESIRES: KNOWS_ABOUT_ITEM
18 | LOOP_UNTIL: NEVER
19 | EXECUTE: PICK_UP_ITEM
20 |
21 | [ACTION_FIND_ITEM]
22 | SATISFIES: KNOWS_ABOUT_ITEM
23 | DESIRES: HAS_NON_RELAXED_GOAL
24 | LOOP_UNTIL: HAS_WANTED_ITEMS
--------------------------------------------------------------------------------
/alife/alife_cover.py:
--------------------------------------------------------------------------------
1 | from globals import *
2 |
3 | import life as lfe
4 |
5 | import judgement
6 | import movement
7 | import bad_numbers
8 | import sight
9 | import brain
10 |
11 | import logging
12 |
13 | def tick(life):
14 | _threats = judgement.get_threats(life, ignore_escaped=2)
15 |
16 | if not _threats:
17 | return True
18 |
19 | for target in [LIFE[t] for t in _threats]:
20 | if bad_numbers.distance(life['pos'], brain.knows_alife(life, target)['last_seen_at']) >= sight.get_vision(life):
21 | _threats.remove(target['id'])
22 |
23 | return movement.hide(life, _threats)
24 |
--------------------------------------------------------------------------------
/data/missions/zes_glock.dat:
--------------------------------------------------------------------------------
1 | =1
2 | WAIT CAN_SEE_TARGET %TARGET%
3 | SET STARTING_CHUNK GET_CHUNK_KEY
4 | JUMP 2
5 |
6 | =2
7 | WAIT !HAS_DIALOG
8 | EXEC MOVE_TO_TARGET %TARGET%
9 | JUMPIF 3 IS_IN_RANGE_OF_TARGET %TARGET% 5.
10 | LOOP
11 |
12 | =3
13 | EXEC STOP
14 | EXEC DROP_ITEM %ITEM_UID%
15 | EXEC GIVE_MISSION kill_target %TARGET% {"target":%KILL_TARGET%,"location":%LOCATION%}
16 | EXEC GIVE_MISSION deliver_item %TARGET% {"target":%DELIVER_TARGET%,"item_uid":%QUEST_ITEM_UID%}
17 | EXEC TRAVEL_TO_CHUNK %STARTING_CHUNK%
18 | EXEC CREATE_MISSION retrieve_item {"item_uid":%QUEST_ITEM_UID%}
19 | COMPLETE
20 |
--------------------------------------------------------------------------------
/tools/templates/group.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | ReactorWatch - Group {{ group_id }}
4 |
5 |
6 | Group {{ group_id }}
7 |
17 |
18 |
--------------------------------------------------------------------------------
/tools/templates/camp.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | ReactorWatch - Camp {{ camp.id }}
4 |
5 |
6 | Camp {{ camp.id }}
7 |
16 |
17 |
--------------------------------------------------------------------------------
/data/items/white_shirt.json:
--------------------------------------------------------------------------------
1 | {"name": "white t-shirt",
2 | "prefix": "a",
3 | "type": "clothing",
4 | "icon": "T",
5 | "description": "A plain, white t-shirt.",
6 | "attaches_to": "lshoulder|rshoulder|chest|stomach",
7 | "flags": "CAN_WEAR|CANSTACK|CAN_BURN|CAN_SOAK",
8 | "craft": [{"name": "Make cloth", "type": "create_item", "requirements": [], "time": 20, "difficulty": {"engineering": 3}, "create_item": {"white cloth": {"failure": {"amount": 2}, "partial_success": {"amount": 4}, "success": {"amount": 6}}}, "strings": {"failure": "You struggle tearing the cloth, destroying it.", "partial_success": "You rip some of the cloth, wasting it.", "success": "You tear the cloth evenly."}}],
9 | "size": "1x1",
10 | "thickness": 1,
11 | "material": "cloth"}
12 |
--------------------------------------------------------------------------------
/data/items/blue_shirt.json:
--------------------------------------------------------------------------------
1 | {"name": "blue t-shirt",
2 | "prefix": "a",
3 | "type": "clothing",
4 | "icon": "T",
5 | "color": [[0, 0, 150], null],
6 | "description": "A plain, blue t-shirt.",
7 | "attaches_to": "lshoulder|rshoulder|chest|stomach",
8 | "flags": "CAN_WEAR|CANSTACK|CAN_BURN|CAN_SOAK",
9 | "craft": [{"name": "Make cloth", "type": "create_item", "requirements": [], "time": 20, "difficulty": {"engineering": 3}, "create_item": {"white cloth": {"failure": {"amount": 2}, "partial_success": {"amount": 4}, "success": {"amount": 6}}}, "strings": {"failure": "You struggle tearing the cloth, destroying it.", "partial_success": "You rip some of the cloth, wasting it.", "success": "You tear the cloth evenly."}}],
10 | "size": "1x1",
11 | "thickness": 1,
12 | "material": "cloth"}
--------------------------------------------------------------------------------
/alife/alife_escape.py:
--------------------------------------------------------------------------------
1 | from globals import *
2 |
3 | import life as lfe
4 |
5 | import judgement
6 | import movement
7 | import sight
8 | import brain
9 |
10 | import logging
11 |
12 | STATE = 'hiding'
13 | TIER = TIER_COMBAT-.2
14 |
15 | def conditions(life, alife_seen, alife_not_seen, targets_seen, targets_not_seen, source_map):
16 | RETURN_VALUE = STATE_UNCHANGED
17 |
18 | if not lfe.execute_raw(life, 'state', 'hide'):
19 | if life['state'] == STATE:
20 | lfe.clear_actions(life)
21 |
22 | return False
23 |
24 | if not life['state'] == STATE:
25 | RETURN_VALUE = STATE_CHANGE
26 |
27 | return RETURN_VALUE
28 |
29 | def tick(life):
30 | _threats = judgement.get_threats(life, limit_distance=sight.get_vision(life))
31 |
32 | return movement.escape(life, _threats)
33 |
--------------------------------------------------------------------------------
/data/items/trenchcoat.json:
--------------------------------------------------------------------------------
1 | {"name": "trenchcoat",
2 | "prefix": "a",
3 | "type": "clothing",
4 | "icon": "E",
5 | "color": [[200, 175, 175], null],
6 | "description": "A long trenchcoat.",
7 | "attaches_to": "lshoulder|rshoulder|chest|stomach",
8 | "flags": "CAN_WEAR|CANSTACK|CAN_BURN|CAN_SOAK",
9 | "craft": [{"name": "Make cloth", "type": "create_item", "requirements": [], "time": 20, "difficulty": {"engineering": 3}, "create_item": {"white cloth": {"failure": {"amount": 2}, "partial_success": {"amount": 4}, "success": {"amount": 6}}}, "strings": {"failure": "You struggle tearing the cloth, destroying it.", "partial_success": "You rip some of the cloth, wasting it.", "success": "You tear the cloth evenly."}}],
10 | "size": "1x6",
11 | "capacity": "1x6",
12 | "thickness": 4,
13 | "material": "cloth"}
14 |
15 |
--------------------------------------------------------------------------------
/alife/alife_guard.py:
--------------------------------------------------------------------------------
1 | from globals import *
2 |
3 | import life as lfe
4 |
5 | import judgement
6 | import movement
7 | import brain
8 |
9 | import logging
10 |
11 | STATE = 'guarding'
12 | TIER = TIER_COMBAT+.1
13 |
14 | def conditions(life, alife_seen, alife_not_seen, targets_seen, targets_not_seen, source_map):
15 | RETURN_VALUE = STATE_UNCHANGED
16 |
17 | if not lfe.execute_raw(life, 'state', 'guard'):
18 | if life['state'] == STATE:
19 | lfe.clear_actions(life)
20 |
21 | return False
22 |
23 | if not life['state'] == STATE:
24 | RETURN_VALUE = STATE_CHANGE
25 |
26 | return RETURN_VALUE
27 |
28 | def tick(life, alife_seen, alife_not_seen, targets_seen, targets_not_seen, source_map):
29 | _target = judgement.get_target_to_guard(life)
30 |
31 | movement.find_target(life, _target, call=False)
32 |
--------------------------------------------------------------------------------
/data/prefabs/test.json:
--------------------------------------------------------------------------------
1 | {"map": [[[null, null, null, null, null], [null, null, null, null, null], [null, null, null, null, null], [null, null, null, null, null], [null, null, null, null, null]], [[null, null, null, null, null], [null, null, null, null, null], [null, null, null, null, null], [null, null, null, null, null], [null, null, null, null, null]], [[null, null, null, null, null], [null, null, null, null, null], [null, null, null, null, null], [null, null, null, null, null], [null, null, null, null, null]], [[null, null, null, null, null], [null, null, null, null, null], [null, null, null, null, null], [null, null, null, null, null], [null, null, null, null, null]], [[null, null, null, null, null], [null, null, null, null, null], [null, null, null, null, null], [null, null, null, null, null], [null, null, null, null, null]]], "size": [5, 5, 5]}
--------------------------------------------------------------------------------
/locks.py:
--------------------------------------------------------------------------------
1 | from globals import LOCKS
2 |
3 |
4 | def create_lock(name, locked=False):
5 | if name in LOCKS:
6 | raise Exception('Lock with name \'%s\' already exists.' % name)
7 |
8 | LOCKS[name] = {'locked': locked, 'lock_reason': ''}
9 |
10 | return LOCKS[name]
11 |
12 | def get_lock(lock_name):
13 | if not lock_name in LOCKS:
14 | raise Exception('Lock with name \'%s\' doest not exist.' % lock_name)
15 |
16 | return LOCKS[lock_name]
17 |
18 | def is_locked(lock_name):
19 | return get_lock(lock_name)['locked']
20 |
21 | def lock(lock_name, reason=''):
22 | get_lock(lock_name)['locked'] = True
23 |
24 | if reason:
25 | print '%s: %s' % (lock_name, reason)
26 |
27 | def unlock(lock_name, reason=''):
28 | get_lock(lock_name)['locked'] = False
29 |
30 | if reason:
31 | print '%s: %s' % (lock_name, reason)
32 |
--------------------------------------------------------------------------------
/data/items/brown_hoodie.json:
--------------------------------------------------------------------------------
1 | {"name": "brown hoodie",
2 | "prefix": "a",
3 | "type": "clothing",
4 | "icon": "H",
5 | "color": [[94, 75, 47], null],
6 | "description": "A brown pull-over jacket with hood.",
7 | "attaches_to": "lshoulder|rshoulder|chest|stomach|lupperarm|lelbow|lforearm|rupperarm|relbow|rforearm|head",
8 | "flags": "CAN_WEAR|CANSTACK|CAN_BURN|CAN_SOAK",
9 | "craft": [{"name": "Make cloth", "type": "create_item", "requirements": [], "time": 20, "difficulty": {"engineering": 3}, "create_item": {"white cloth": {"failure": {"amount": 2}, "partial_success": {"amount": 6}, "success": {"amount": 8}}}, "strings": {"failure": "You struggle tearing the cloth, destroying it.", "partial_success": "You rip some of the cloth, wasting it.", "success": "You tear the cloth evenly."}}],
10 | "size": "2x2",
11 | "capacity": "1x1",
12 | "thickness": 1,
13 | "material": "cloth"}
14 |
--------------------------------------------------------------------------------
/alife/alife_group.py:
--------------------------------------------------------------------------------
1 | from globals import *
2 |
3 | import life as lfe
4 |
5 | import judgement
6 | import movement
7 | import groups
8 | import speech
9 | import action
10 | import events
11 | import brain
12 | import stats
13 | import jobs
14 |
15 |
16 | def setup(life):
17 | if not life['group']:
18 | #if not stats.desires_to_create_group(life):
19 | # return False
20 |
21 | #groups.create_group(life)
22 | return False
23 |
24 | if not groups.is_leader_of_any_group(life):
25 | return False
26 |
27 | _group = groups.get_group(life, life['group'])
28 |
29 | #groups.manage_jobs(life, life['group'])
30 | #groups.manage_territory(life, life['group'])
31 | groups.manage_combat(life, life['group'])
32 | groups.manage_raid(life, life['group'])
33 | #groups.manage_resources(life, life['group'])
34 | #groups.manage_known_groups(life, life['group'])
35 | #groups.manage_combat(life, life['group'])
--------------------------------------------------------------------------------
/tests/M01_data_structure.py:
--------------------------------------------------------------------------------
1 | GRASS_TILE = {'id':'grass',
2 | 'icon':'.',
3 | 'color':'green',
4 | 'burnable':True,
5 | 'cost':1}
6 |
7 | DIRT_TILE = {'id':'dirt',
8 | 'icon':'.',
9 | 'color':'brown',
10 | 'burnable':False,
11 | 'cost':2}
12 |
13 | TILES = [GRASS_TILE,DIRT_TILE]
14 | MAP = []
15 |
16 | def create_tile(tile):
17 | _ret_tile = {}
18 | _ret_tile['id'] = tile['id']
19 |
20 | _ret_tile['items'] = []
21 | _ret_tile['fire'] = 0
22 |
23 | return _ret_tile
24 |
25 | def get_raw_tile(tile):
26 | for _tile in TILES:
27 | if _tile['id'] == tile['id']:
28 | return _tile
29 |
30 | raise Exception
31 |
32 | for x in range(400):
33 | _y = []
34 | for y in range(400):
35 | _y.append(create_tile(DIRT_TILE))
36 |
37 | MAP.append(_y)
38 |
39 | print MAP[3][3],get_raw_tile(MAP[3][3])['cost']
40 |
41 | import sys
42 | print 'Map size (bytes):',sys.getsizeof(MAP)
--------------------------------------------------------------------------------
/tools/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | ReactorWatch - Debug
4 |
5 |
6 | Stats
7 | - Memories: {{ stats.total_memories }}
8 | - Active life: {{ stats.active_life }}
9 | - Groups: {{ groups|count }}
10 | - Camps: {{ camps|count }}
11 |
12 | Life
13 |
14 | {% for id in life %}
15 | - {{ id }}
16 | {% endfor %}
17 |
18 | Groups
19 |
24 | Camps
25 |
26 | {% for camp in camps %}
27 | - {{ camp }}
28 | {% endfor %}
29 |
30 |
31 |
--------------------------------------------------------------------------------
/alife/alife_search.py:
--------------------------------------------------------------------------------
1 | from globals import *
2 |
3 | import life as lfe
4 |
5 | import judgement
6 | import movement
7 | import bad_numbers
8 | import speech
9 | import camps
10 | import brain
11 | import jobs
12 |
13 | import logging
14 |
15 | STATE = 'searching'
16 | TIER = TIER_COMBAT
17 |
18 | def conditions(life, alife_seen, alife_not_seen, targets_seen, targets_not_seen, source_map):
19 | RETURN_VALUE = STATE_UNCHANGED
20 |
21 | if not life['state'] == STATE:
22 | RETURN_VALUE = STATE_CHANGE
23 |
24 | _lost_targets = judgement.get_combat_targets(life, escaped_only=True)
25 | brain.store_in_memory(life, 'lost_targets', _lost_targets)
26 |
27 | if not _lost_targets:
28 | return False
29 | else:
30 | print life['name'], _lost_targets
31 |
32 | return RETURN_VALUE
33 |
34 | def tick(life):
35 | _lost_targets = judgement.get_threats(life, escaped_only=True, ignore_escaped=2)
36 |
37 | movement.search_for_target(life, _lost_targets[0])
--------------------------------------------------------------------------------
/data/life/dog.goap:
--------------------------------------------------------------------------------
1 | [GOAL_DISCOVER]
2 | DESIRE: HAS_THREATS
3 | TIER: RELAXED
4 |
5 | [GOAL_FOLLOW]
6 | DESIRE: !HAS_TARGET_TO_FOLLOW
7 | TIER: URGENT
8 |
9 | [GOAL_GUARD]
10 | DESIRE: !HAS_FOCUS_POINT
11 | TIER: SURVIVAL
12 |
13 | [GOAL_KILL_THREAT]
14 | DESIRE: !HAS_THREATS,-!HAS_VISIBLE_THREAT
15 | TIER: COMBAT
16 |
17 | #[GOAL_SEARCH_FOR_TARGET]
18 | #DESIRE: !HAS_LOST_THREAT,-HAS_VISIBLE_THREAT,-!WEAPON_EQUIPPED_AND_READY
19 | #TIER: TACTIC
20 |
21 |
22 | [ACTION_WANDER]
23 | SATISFIES: HAS_THREATS
24 | LOOP_UNTIL: NEVER
25 | EXECUTE: WANDER
26 |
27 | [ACTION_FOLLOW]
28 | SATISFIES: !HAS_TARGET_TO_FOLLOW
29 | LOOP_UNTIL: NEVER
30 | EXECUTE: FOLLOW_TARGET
31 |
32 | [ACTION_GO_TO]
33 | SATISFIES: !HAS_FOCUS_POINT
34 | LOOP_UNTIL: NEVER
35 | EXECUTE: GUARD_FOCUS_POINT
36 |
37 | [ACTION_MELEE_THREAT]
38 | SATISFIES: !HAS_THREATS,-MELEE_READY
39 | EXECUTE: MELEE_ATTACK
40 | LOOP_UNTIL: NEVER
41 |
42 | [ACTION_IDLE]
43 | SATISFIES: IS_IDLE
44 | EXECUTE: NEVER
45 | LOOP_UNTIL: ALWAYS
46 |
--------------------------------------------------------------------------------
/alife/alife_manage_targets.py:
--------------------------------------------------------------------------------
1 | from globals import *
2 | import life as lfe
3 |
4 | import alife
5 | import raids
6 |
7 | TIER = TIER_PASSIVE
8 |
9 | def conditions(life, alife_seen, alife_not_seen, targets_seen, targets_not_seen, source_map):
10 | return RETURN_SKIP
11 |
12 | def add_raid_targets(life):
13 | for raider in [LIFE[r] for r in raids.get_raiders(life['camp'])]:
14 | if raider['id'] == life['id']:
15 | continue
16 |
17 | if not alife.brain.knows_alife(life, raider):
18 | alife.brain.meet_alife(life, raider)
19 |
20 | if not lfe.get_memory(life, matches={'text': 'target_is_raiding', 'target': raider['id'], 'camp': life['camp']}):
21 | lfe.memory(life,
22 | 'target_is_raiding',
23 | target=raider['id'],
24 | camp=life['camp'])
25 |
26 |
27 | def tick(life, alife_seen, alife_not_seen, targets_seen, targets_not_seen, source_map):
28 | if life['camp']:
29 | if raids.camp_has_raid(life['camp']):
30 | add_raid_targets(life)
--------------------------------------------------------------------------------
/alife/snapshots.py:
--------------------------------------------------------------------------------
1 | import life as lfe
2 |
3 | import logging
4 | import time
5 |
6 | def update_self_snapshot(life,snapshot):
7 | life['snapshot'] = snapshot
8 |
9 | def update_snapshot_of_target(life,target,snapshot):
10 | life['know'][target['id']]['snapshot'].update(snapshot)
11 |
12 | #logging.debug('%s updated their snapshot of %s.' % (' '.join(life['name']), ' '.join(target['name'])))
13 |
14 | def create_snapshot(life):
15 | _snapshot = {'damage': lfe.get_damage(life),
16 | 'appearance': 0,
17 | 'visible_items': [],
18 | 'generated': time.time()}
19 |
20 | for item in lfe.get_all_visible_items(life):
21 | _snapshot['visible_items'].append(str(item))
22 |
23 | return _snapshot
24 |
25 | def process_snapshot(life,target):
26 | if life['know'][target['id']]['snapshot'] == target['snapshot']:
27 | return False
28 |
29 | _ss = target['snapshot'].copy()
30 |
31 | update_snapshot_of_target(life,target,_ss)
32 |
33 | return True
34 |
35 | def check_snapshot(life,target):
36 | return life['know'][target['id']]['snapshot']
37 |
--------------------------------------------------------------------------------
/license.txt:
--------------------------------------------------------------------------------
1 | Copyright (C) 2013 Luke Martin (flags)
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/alife/alife_hidden.py:
--------------------------------------------------------------------------------
1 | #This is intended to be an example of how the new ALife
2 | #system works.
3 | from globals import *
4 |
5 | import life as lfe
6 |
7 | import judgement
8 | import movement
9 | import combat
10 |
11 | import logging
12 |
13 | STATE = 'hidden'
14 | TIER = TIER_COMBAT-.3
15 |
16 | def conditions(life, alife_seen, alife_not_seen, targets_seen, targets_not_seen, source_map):
17 | RETURN_VALUE = STATE_UNCHANGED
18 |
19 | if not lfe.execute_raw(life, 'state', 'hidden'):
20 | return False
21 |
22 | if not life['state'] == STATE:
23 | RETURN_VALUE = STATE_CHANGE
24 |
25 | return RETURN_VALUE
26 |
27 | def tick(life, alife_seen, alife_not_seen, targets_seen, targets_not_seen, source_map):
28 | _threat = judgement.get_nearest_threat(life)
29 |
30 | if not 'hiding' in life['state_flags']:
31 | movement.hide(life, _threat)
32 | life['state_flags']['hiding'] = True
33 | return True
34 |
35 | _weapon = combat.get_best_weapon(life)
36 |
37 | if _weapon:
38 | if not combat.weapon_equipped_and_ready(life):
39 | combat._equip_weapon(life, _weapon['weapon']['uid'], _weapon['feed']['uid'])
40 |
--------------------------------------------------------------------------------
/alife/alife_discover.py:
--------------------------------------------------------------------------------
1 | from globals import *
2 |
3 | import life as lfe
4 |
5 | import judgement
6 | import survival
7 | import chunks
8 | import sight
9 | import brain
10 | import smp
11 |
12 | import logging
13 |
14 | def tick(life):
15 | if not lfe.execute_raw(life, 'discover', 'discover_type'):
16 | _lost_method = lfe.execute_raw(life, 'discover', 'when_lost')
17 | if _lost_method:
18 | if not life['path'] or not brain.retrieve_from_memory(life, 'discovery_lock'):
19 | if not 'scanned_chunks' in life['state_flags']:
20 | life['state_flags']['scanned_chunks'] = []
21 |
22 | sight.scan_surroundings(life, _chunks=brain.get_flag(life, 'visible_chunks'), ignore_chunks=life['state_flags']['scanned_chunks'])
23 |
24 | _explore_chunk = chunks.find_best_chunk(life, ignore_starting=True, ignore_time=True, lost_method=_lost_method, only_recent=True)
25 | brain.store_in_memory(life, 'discovery_lock', True)
26 | brain.store_in_memory(life, 'explore_chunk', _explore_chunk)
27 |
28 | if not _explore_chunk:
29 | brain.flag(life, 'lost')
30 | return False
31 |
32 | survival.explore_known_chunks(life)
33 | else:
34 | return False
35 |
--------------------------------------------------------------------------------
/alife/alife_explore.py:
--------------------------------------------------------------------------------
1 | #TODO: THIS CAN BE IDLE BEHAVIOR
2 |
3 | from globals import *
4 |
5 | import life as lfe
6 |
7 | import judgement
8 | import survival
9 | import chunks
10 | import sight
11 | import brain
12 |
13 | import logging
14 |
15 | STATE = 'exploring'
16 | TIER = TIER_EXPLORE-.2
17 |
18 | def conditions(life, alife_seen, alife_not_seen, targets_seen, targets_not_seen, source_map):
19 | return False
20 |
21 | RETURN_VALUE = STATE_UNCHANGED
22 |
23 | if not lfe.execute_raw(life, 'state', 'explore'):
24 | return False
25 |
26 | if not life['state'] == STATE:
27 | RETURN_VALUE = STATE_CHANGE
28 |
29 | #_leading_target = judgement.get_leading_target(life)
30 | #if _leading_target:
31 | # _known = brain.knows_alife_by_id(life, _leading_target)
32 | #
33 | # print _leading_target
34 |
35 | _explore_chunk = chunks.find_best_chunk(life, ignore_time=True)
36 | brain.store_in_memory(life, 'explore_chunk', _explore_chunk)
37 |
38 | if not _explore_chunk:
39 | return False
40 |
41 | return RETURN_VALUE
42 |
43 | def tick(life, alife_seen, alife_not_seen, targets_seen, targets_not_seen, source_map):
44 | if survival.explore_known_chunks(life):
45 | return True
46 |
--------------------------------------------------------------------------------
/alife/alife_shelter.py:
--------------------------------------------------------------------------------
1 | from globals import *
2 |
3 | import life as lfe
4 |
5 | import references
6 | import judgement
7 | import chunks
8 | import goals
9 | import maps
10 |
11 | import random
12 |
13 | STATE = 'shelter'
14 | TIER = TIER_SURVIVAL-.1
15 |
16 | def get_tier(life):
17 | if not lfe.execute_raw(life, 'discover', 'desires_shelter') and lfe.execute_raw(life, 'state', 'shelter'):
18 | return TIER_IDLE-.1
19 |
20 | return TIER
21 |
22 | def tick(life):
23 | if not 'shelter' in life['state_flags']:
24 | life['state_flags']['shelter'] = judgement.get_best_shelter(life)
25 |
26 | if not life['state_flags']['shelter'] in life['known_chunks']:
27 | judgement.judge_chunk(life, life['state_flags']['shelter'])
28 |
29 | if not chunks.get_flag(life, life['state_flags']['shelter'], 'shelter_cover'):
30 | return False
31 |
32 | if not list(life['pos'][:2]) in chunks.get_flag(life, life['state_flags']['shelter'], 'shelter_cover'):
33 | if not lfe.path_dest(life) or (not chunks.position_is_in_chunk(lfe.path_dest(life), life['state_flags']['shelter'])):
34 | _cover = chunks.get_flag(life, life['state_flags']['shelter'], 'shelter_cover')
35 | lfe.walk_to(life, random.choice(_cover))
36 |
--------------------------------------------------------------------------------
/alife/alife_surrender.py:
--------------------------------------------------------------------------------
1 | from globals import *
2 |
3 | import life as lfe
4 |
5 | import judgement
6 | import bad_numbers
7 | import speech
8 | import brain
9 | import stats
10 |
11 | import logging
12 |
13 | STATE = 'surrender'
14 | TIER = TIER_SUBMIT
15 |
16 | STATE_ICONS[STATE] = chr(25)
17 |
18 | def conditions(life, alife_seen, alife_not_seen, targets_seen, targets_not_seen, source_map):
19 | RETURN_VALUE = STATE_UNCHANGED
20 |
21 | if not lfe.execute_raw(life, 'state', 'surrender'):
22 | return False
23 |
24 | if not life['state'] == STATE:
25 | lfe.stop(life)
26 | lfe.say(life, '@n gives up.', action=True)
27 |
28 | RETURN_VALUE = STATE_CHANGE
29 |
30 | return RETURN_VALUE
31 |
32 | def tick(life, alife_seen, alife_not_seen, targets_seen, targets_not_seen, source_map):
33 | if lfe.ticker(life, 'call_for_help', 160, fire=True):
34 | _target = judgement.get_nearest_threat(life)
35 | _knows = brain.knows_alife_by_id(life, _target)
36 | if _target and judgement.get_nearest_trusted_target(life):
37 | if _knows:
38 | speech.announce(life, 'attacked_by_hostile', public=True, target_id=_target, last_seen_at=_knows['last_seen_at'])
39 | else:
40 | speech.announce(life, 'attacked_by_hostile', public=True, target_id=_target)
41 |
--------------------------------------------------------------------------------
/data/life/dog.dat:
--------------------------------------------------------------------------------
1 | [TALK]
2 | DESIRES_INTERACTION:IS_SAFE
3 | DESIRES_CONVERSATION_WITH:IS_SAME_SPECIES
4 | CAN_TALK_TO:IS_SAME_SPECIES
5 | BATTLE_CRY:NEVER
6 |
7 | [JUDGE]
8 | TRUST:!IS_SAME_SPECIES{TARGET.TRUST-15},!IS_SAME_SPECIES{TARGET.DANGER+15},IS_NERVOUS{TARGET.TRUST-1}
9 | FACTORS:@ALWAYS
10 | BREAK_TRUST:@NEVER
11 | NERVOUS:%IS_NIGHT,!IS_FAMILY
12 | IS_THREAT_IF:HAS_ATTACKED_SELF|HAS_ATTACKED_TRUSTED{TARGET.DANGER+5}|!IS_SAME_SPECIES,IS_NERVOUS
13 |
14 | [SEARCH]
15 | JUDGE:*DISTANCE_TO_POS
16 |
17 | [DISCOVER]
18 | DISCOVER_TYPE:EXPLORE_UNKNOWN_CHUNKS
19 | REMEMBER_SHELTER:ALWAYS
20 | DESIRES_SHELTER:IS_SAFE,%IS_NIGHT,!IS_IN_GROUP|IS_GROUP_LEADER,!IS_GROUP_MOTIVATED_FOR_CRIME|IS_IN_GROUP,GROUP_HAS_SHELTER,!%IS_NIGHT,IS_GROUP_MOTIVATED_FOR_CRIME
21 | WHEN_LOST:"FURTHEST"
22 |
23 | [FOLLOW]
24 | FOLLOW_TARGET_IF:IS_IN_SAME_GROUP,IS_TARGET_GROUP_LEADER,IS_AWAKE
25 |
26 | [GUARD]
27 | GUARD_TARGET_IF:IS_IN_SAME_GROUP,IS_TARGET_GROUP_LEADER,!IS_AWAKE
28 |
29 | [GROUP]
30 | WANTS_GROUP_MEMBER:@NEVER
31 | CREATE_GROUP:@NEVER
32 |
33 | [CAMP]
34 | CAN_CAMP:@NEVER
35 |
36 | [SAFETY]
37 | INTIMIDATED:@NEVER
38 |
39 | [MEMORY]
40 | CACHE_DROP:"investigate_chunk"
41 |
42 | [ITEMS]
43 | GUN:"equip"
44 |
45 | [COMBAT]
46 | SEEK_COMBAT_IF:%IS_NIGHT
47 | TRUST_LOWER_THAN:"-3"
48 | RANGED:NEVER
49 | RANGED_READY:NEVER
50 | MELEE:ALWAYS
51 | MELEE_READY:ALWAYS
52 |
53 | #What target we engage (CLOSEST, HIGHEST, LOWEST)
54 | ENGAGE:"CLOSEST"
55 |
--------------------------------------------------------------------------------
/data/life/night_terror.dat:
--------------------------------------------------------------------------------
1 | [TALK]
2 | DESIRES_INTERACTION:NEVER
3 | DESIRES_CONVERSATION_WITH:IS_SAME_SPECIES
4 | CAN_TALK_TO:IS_SAME_SPECIES
5 | BATTLE_CRY:"action"
6 | BATTLE_CRY_ACTION:"@n lurches forward."
7 |
8 | [JUDGE]
9 | TRUST:IS_SAME_SPECIES{TARGET.TRUST+5}
10 | FACTORS:IS_SAME_SPECIES
11 | BREAK_TRUST:IS_NERVOUS{TARGET.DANGER+6}
12 | NERVOUS:@NEVER
13 | AGGRAVATED:%IS_NIGHT,!IS_SAME_SPECIES
14 |
15 | [MOVEMENT]
16 | FOLLOW:@NEVER
17 |
18 | [SEARCH]
19 | JUDGE:*DISTANCE_TO_POS
20 |
21 | [DISCOVER]
22 | DISCOVER_TYPE:NEVER
23 | REMEMBER_SHELTER:NEVER
24 | DESIRES_SHELTER:NEVER
25 | WHEN_LOST:"FURTHEST"
26 |
27 | [GROUP]
28 | WANTS_GROUP_MEMBER:@NEVER
29 | CREATE_GROUP:NEVER
30 |
31 | [CAMP]
32 | CAN_CAMP:NEVER
33 |
34 | [STATE]
35 | EXPLORE:ALWAYS
36 | DISCOVER:NEVER
37 | SURRENDER:NEVER
38 | NEEDS:NEVER
39 | SHELTER:IS_SAFE
40 | COVER:NEVER
41 | HIDE:NEVER
42 | HIDDEN:NEVER
43 |
44 | [SAFETY]
45 | INTIMIDATED:NEVER
46 |
47 | [MEMORY]
48 | TEMP:ALWAYS
49 |
50 | [COMBAT]
51 | SEEK_COMBAT_IF:ALWAYS
52 | TRUST_LOWER_THAN:"0"
53 | RANGED:NEVER
54 | RANGED_READY:NEVER
55 | MELEE:*CAN_BITE,*CAN_SCRATCH
56 | MELEE_READY:ALWAYS
57 |
58 | #What to use for this engagement. LIMB[arg] or WEAPON
59 | #The bit inside the curly braces defines the type of combat: MELEE or RANGE
60 | USE:LIMB[JAW]{MELEE}
61 |
62 | #What target we engage (CLOSEST, HIGHEST, LOWEST)
63 | ENGAGE:CLOSEST
64 |
65 | #What we do to the target: KILL, MAIME
66 | TARGET:KILL
67 |
--------------------------------------------------------------------------------
/alife/alife_needs.py:
--------------------------------------------------------------------------------
1 | from globals import *
2 |
3 | import life as lfe
4 |
5 | import judgement
6 | import movement
7 | import survival
8 | import brain
9 |
10 |
11 | def setup(life):
12 | _needs_to_meet = []
13 | _needs_to_satisfy = []
14 | _needs_unmet = []
15 |
16 | for need in life['needs'].values():
17 | if not survival.needs_to_satisfy(life, need):
18 | continue
19 |
20 | if survival.can_satisfy(life, need):
21 | _needs_to_satisfy.append(need)
22 |
23 | if not survival.can_satisfy(life, need) and not survival.can_potentially_satisfy(life, need):
24 | _needs_unmet.append(need)
25 | continue
26 |
27 | if not need in _needs_to_satisfy and not need in _needs_to_satisfy:
28 | _needs_to_meet.append(need)
29 |
30 | brain.store_in_memory(life, 'needs_to_meet', _needs_to_meet)
31 | brain.store_in_memory(life, 'needs_to_satisfy', _needs_to_satisfy)
32 | brain.store_in_memory(life, 'needs_unmet', _needs_unmet)
33 |
34 | if not _needs_to_meet and not _needs_to_satisfy:
35 | return False
36 |
37 | def tick(life):
38 | if life['actions']:
39 | return True
40 |
41 | _needs_to_meet = brain.retrieve_from_memory(life, 'needs_to_meet')
42 |
43 | for need in _needs_to_meet:
44 | movement.collect_nearby_wanted_items(life, matches=need['match'], only_visible=False)
45 | break
46 |
47 | _needs_to_satisfy = brain.retrieve_from_memory(life, 'needs_to_satisfy')
48 |
49 | for need in _needs_to_satisfy:
50 | survival.satisfy(life, need)
51 |
--------------------------------------------------------------------------------
/timers.py:
--------------------------------------------------------------------------------
1 | from globals import WORLD_INFO, ITEMS, LIFE
2 |
3 | import alife
4 |
5 | def is_life(entity):
6 | _life = (not 'prefix' in entity)
7 |
8 | if _life:
9 | _id_key = 'id'
10 | else:
11 | _id_key = 'uid'
12 |
13 | return _life, _id_key
14 |
15 | def create(entity, action, time):
16 | _life, _id_key = is_life(entity)
17 |
18 | _new_timer = {'action': action,
19 | 'time': WORLD_INFO['ticks']+time,
20 | 'owner': entity[_id_key],
21 | 'life': _life}
22 |
23 | _i = 0
24 | for timer in WORLD_INFO['timers']:
25 | if _new_timer['time'] > time['time']:
26 | WORLD_INFO['timers'].insert(_i, _new_timer)
27 | return True
28 |
29 | _i += 1
30 |
31 | WORLD_INFO['timers'].append(_new_timer)
32 |
33 | def remove_by_owner(entity):
34 | _life, _id_key = is_life(entity)
35 |
36 | _remove = []
37 | for timer in WORLD_INFO['timers']:
38 | if timer['owner'] in LIFE:
39 | if LIFE[timer['owner']] == entity:
40 | _remove.append(timer)
41 | else:
42 | if ITEMS[timer['owner']] == entity:
43 | _remove.append(timer)
44 |
45 | while _remove:
46 | WORLD_INFO['timers'].remove(_remove.pop())
47 |
48 | def tick():
49 | if not WORLD_INFO['timers']:
50 | return False
51 |
52 | while WORLD_INFO['ticks'] == WORLD_INFO['timers'][0]['time']:
53 | _event = WORLD_INFO['timers'][0]
54 |
55 | if _event['life']:
56 | _owner = LIFE[_event['owner']]
57 | else:
58 | _owner = ITEMS[_event['owner']]
59 |
60 | alife.action.execute_small_script(_owner, _event['action'])
61 |
62 | if _event in WORLD_INFO['timers']:
63 | WORLD_INFO['timers'].remove(_event)
64 |
65 | if not WORLD_INFO['timers']:
66 | break
67 |
--------------------------------------------------------------------------------
/docs/mission_planning.md:
--------------------------------------------------------------------------------
1 | # Dynamic storyteller design doc
2 | The storyteller's job changes over the course of a character's life.
3 |
4 | ## Early-game
5 | The player is dropped into a random situation. Their task it to work their
6 | way out, but in actuality the game is watching how they react and shaping
7 | the content on-the-fly. Right now the player's alignment to major factions
8 | is the only factor decided by this process. Moving forward, opportunities
9 | to expand on this should be noted and explored.
10 |
11 | #### So what's the point?
12 | Even if no content-changing events take place, the player is still being led
13 | one way or another based on how they react. We'll need to account for all
14 | possible paths through a situation in order for the player to not end up lost
15 | and afraid.
16 |
17 | #### A note about free will
18 | The player can choose to just leg it out of any of these situations. I'm unsure
19 | how to handle this right now, since it is possible to survive with just food and
20 | water.
21 |
22 | ### Situation 1
23 | The player wakes up next to 1+ dead bodies. The bodies' loot should be
24 | randomized amounts of the LONER loadout. The player is given no items and should
25 | instead pick them up off the ground/bodies. A person arrives and offers to lead
26 | the player to a Loner camp.
27 |
28 | Special event: A bandit group comes in to loot the bodies. This only affects the
29 | urgency of the situation (dialog with the guy helping also changes.)
30 |
31 | #### Paths
32 | * **A)** The player follows the Loner to a camp ->
33 | * **B)** The player ignores the Loner -> Unsure (see "A note about free will")
34 |
35 | The player is taught:
36 |
37 | * Inventory management.
38 | * Group (if they
39 |
40 |
--------------------------------------------------------------------------------
/tools/ReactorWatch.py:
--------------------------------------------------------------------------------
1 | #This tool was rushed together over the course of an hour or so. Be gentle.
2 |
3 | from flask import Flask, render_template, request
4 |
5 | import threading
6 | import socket
7 | import json
8 |
9 | app = Flask(__name__)
10 |
11 | def request(request, value=None):
12 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
13 | sock.settimeout(5)
14 | sock.connect(('127.0.0.1', 3335))
15 | sock.sendall(json.dumps({'type': 'get', 'what': request, 'value': value}))
16 | data = json.loads(sock.recv(9048))
17 | sock.close()
18 |
19 | return data
20 |
21 | @app.route('/memory/')
22 | def memory(life_id):
23 | memories = request('memory', value=int(life_id))
24 |
25 | return render_template('memory.html', life_id=life_id, memories=memories)
26 |
27 | @app.route('/life/')
28 | def life(life_id):
29 | life = request('life', value=int(life_id))
30 | knows = life['know'].values()
31 |
32 | return render_template('life.html', life=life, knows=knows)
33 |
34 | @app.route('/camp/')
35 | def camp(camp_id):
36 | camp = request('camp', value=int(camp_id))
37 |
38 | return render_template('camp.html', camp=camp)
39 |
40 | @app.route('/group/')
41 | def group(group_id):
42 | groups = request('groups')
43 | group = groups[group_id]
44 |
45 | return render_template('group.html', group_id=group_id, group=group)
46 |
47 | @app.route('/')
48 | def index():
49 | groups = request('groups')
50 |
51 | life = request('life_list')
52 | life.sort()
53 |
54 | #camps = request('camp_list')
55 | #camps.sort()
56 |
57 | stats = request('stats')
58 |
59 | return render_template('index.html', stats=stats, life=life, groups=groups)
60 |
61 | if __name__ == '__main__':
62 | app.run(debug=True, port=3336)
--------------------------------------------------------------------------------
/debug.py:
--------------------------------------------------------------------------------
1 | from globals import *
2 |
3 | import weather
4 |
5 | import zones
6 | import alife
7 | import items
8 | import life
9 |
10 | def suicide():
11 | life.kill(LIFE[SETTINGS['following']], 'suicide')
12 |
13 | def kill(life_id):
14 | life.kill(LIFE[life_id], 'suicide')
15 |
16 | def stop():
17 | LIFE[SETTINGS['following']]['path'] = []
18 |
19 | def clean_slate():
20 | for alife in LIFE.values():
21 | if alife['id'] == SETTINGS['controlling'] or alife['dead']:
22 | continue
23 |
24 | life.kill(alife, 'an act of treason')
25 |
26 | def make_hungry(life_id):
27 | LIFE[life_id]['hunger'] = 500
28 |
29 | def world_hunger():
30 | for l in LIFE.values():
31 | l['hunger'] = 500
32 |
33 | def make_thirsty(life_id):
34 | LIFE[life_id]['thirst'] = 500
35 |
36 | def simple_lights():
37 | SETTINGS['draw light'] = False
38 |
39 | def time(time):
40 | WORLD_INFO['real_time_of_day'] = time
41 |
42 | def timescale(scale):
43 | WORLD_INFO['time_scale'] = scale
44 |
45 | def warp(x, y):
46 | LIFE[SETTINGS['controlling']]['pos'][0] = x
47 | LIFE[SETTINGS['controlling']]['pos'][1] = y
48 |
49 | def camps():
50 | alife.camps.debug_camps()
51 |
52 | def food():
53 | items.create_item('corn', position=LIFE[SETTINGS['controlling']]['pos'])
54 |
55 | def drink():
56 | items.create_item('soda', position=LIFE[SETTINGS['controlling']]['pos'])
57 |
58 | def give(item):
59 | items.create_item(item, position=LIFE[SETTINGS['controlling']]['pos'])
60 |
61 | def day():
62 | WORLD_INFO['real_time_of_day'] = 1500
63 |
64 | def night():
65 | WORLD_INFO['real_time_of_day'] = 0
66 |
67 | def toss():
68 | life.push(LIFE[SETTINGS['controlling']], 0, 2)
69 |
70 | def soldier():
71 | LIFE[SETTINGS['following']]['stats']['firearms'] = 10
72 |
73 | def weather():
74 | weather.change_weather()
--------------------------------------------------------------------------------
/crafting.py:
--------------------------------------------------------------------------------
1 | import graphics as gfx
2 | import life as lfe
3 |
4 | import scripting
5 | import bad_numbers
6 | import items
7 |
8 |
9 | def get_items_for_crafting(life):
10 | return [i for i in life['inventory'] if 'craft' in items.get_item_from_uid(i)]
11 |
12 | def get_recipe_difficulty(life, item, recipe):
13 | _difficulty = 0
14 |
15 | for skill in recipe['difficulty']:
16 | if skill in life['stats']:
17 | _difficulty += recipe['difficulty'][skill]-life['stats'][skill]
18 |
19 | return _difficulty
20 |
21 | #TODO: stub
22 | def meets_requirements(life, requirements):
23 | return True
24 |
25 | def perform_recipe(life, item, recipe):
26 | lfe.add_action(life, {'action': 'craft',
27 | 'item_uid': item['uid'],
28 | 'recipe_id': item['craft'].index(recipe)},
29 | 99999,
30 | delay=recipe['time'])
31 |
32 | def execute_recipe(life, item, recipe):
33 | _difficulty = get_recipe_difficulty(life, item, recipe)
34 | _dice_percentage = bad_numbers.roll(2, 5)/(10.0+_difficulty)
35 |
36 | if _dice_percentage >= .75:
37 | _recipe_quality = 'success'
38 | elif _dice_percentage >= .5:
39 | _recipe_quality = 'partial_success'
40 | else:
41 | _recipe_quality = 'failure'
42 |
43 | if recipe['type'] == 'create_item':
44 | for create_item in recipe['create_item']:
45 | if not _recipe_quality in recipe['create_item'][create_item]:
46 | continue
47 |
48 | for i in range(recipe['create_item'][create_item][_recipe_quality]['amount']):
49 | _created_item = items.create_item(create_item)
50 | lfe.add_item_to_inventory(life, _created_item)
51 |
52 | if _recipe_quality in recipe['strings']:
53 | gfx.message(recipe['strings'][_recipe_quality])
54 | else:
55 | gfx.message('You finish crafting.')
56 |
--------------------------------------------------------------------------------
/tools/templates/life.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | ReactorWatch - Life {{ life.id }}
4 |
5 |
6 | Life {{ life.name }}
7 |
8 | - Name: {{ life.name }}
9 | - Memory
10 | - State: {{ life.state }}
11 | - Job: {{ life.job }}
12 | - Group: {{ life.group }}
13 | - Stats:
14 | {% for stat in life.stats %}
15 |
- {{ stat }}: {{ life.stats[stat] }}
16 |
17 | {% endfor %}
18 |
19 | - Flags ({{ life.flags|count }}):
20 | {% for flag in life.flags %}
21 |
- {{ flag }}: {{ life.flags[flag] }}
22 |
23 | {% endfor %}
24 |
25 | - Temp Memory: ({{ life.tempstor|count }}):
26 | {% for memory in life.tempstor %}
27 |
- {{ memory }}: {{ life.tempstor[memory] }}
28 |
29 | {% endfor %}
30 |
31 | - Knows ({{ life.know|count }}):
32 | {% for target in knows %}
33 | {{ target.life }}
34 |
- Trust: {{ target.trust }}
35 | - Danger: {{ target.danger }}
36 | - Influence: {{ target.influence }}
37 | - Flags: {{ target.flags }}
38 | - Impressions: {{ target.impressions }}
39 | - Memory
40 |
41 | {% endfor %}
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/data/life/human.dat:
--------------------------------------------------------------------------------
1 | [STATE]
2 | MANAGING:!WEAPON_EQUIPPED_AND_READY,HAS_POTENTIALLY_USABLE_WEAPON
3 |
4 | [TALK]
5 | DESIRES_INTERACTION:IS_SAFE
6 | DESIRES_CONVERSATION_WITH:IS_SAME_SPECIES
7 | CAN_TALK_TO:IS_SAME_SPECIES
8 | BATTLE_CRY:NEVER
9 |
10 | [JUDGE]
11 | TRUST:!IS_SAME_SPECIES{TARGET.TRUST-15},!IS_SAME_SPECIES{TARGET.DANGER+15}
12 | FACTORS:@ALWAYS
13 | BREAK_TRUST:IS_TRAITOR{TARGET.DANGER+5}|HAS_ATTACKED_TRUSTED{TARGET.DANGER+5}|IS_TARGET_GROUP_HOSTILE{TARGET.DANGER+5}
14 | NERVOUS:@NEVER
15 | IS_THREAT_IF:HAS_ATTACKED_SELF|HAS_ATTACKED_TRUSTED{TARGET.DANGER+5}|IS_TARGET_GROUP_HOSTILE{TARGET.DANGER+5}|IS_TARGET_HOSTILE
16 |
17 | [SEARCH]
18 | JUDGE:*DISTANCE_TO_POS
19 |
20 | [DISCOVER]
21 | DISCOVER_TYPE:EXPLORE_UNKNOWN_CHUNKS
22 | REMEMBER_SHELTER:ALWAYS
23 | DESIRES_SHELTER:IS_SAFE,%IS_NIGHT,!IS_IN_GROUP|IS_GROUP_LEADER,!IS_GROUP_MOTIVATED_FOR_CRIME|IS_IN_GROUP,GROUP_HAS_SHELTER,!%IS_NIGHT,IS_GROUP_MOTIVATED_FOR_CRIME
24 | WHEN_LOST:"FURTHEST"
25 |
26 | [FOLLOW]
27 | FOLLOW_TARGET_IF:IS_IN_SAME_GROUP,IS_TARGET_GROUP_LEADER,IS_AWAKE|!@HAS_NEEDS,CAN_TRUST
28 |
29 | [GUARD]
30 | GUARD_TARGET_IF:IS_IN_SAME_GROUP,IS_TARGET_GROUP_LEADER,!IS_AWAKE|IS_DISARMING,!@HAS_THREATS
31 |
32 | [GROUP]
33 | WANTS_GROUP_MEMBER:@ALWAYS
34 | CREATE_GROUP:@IS_BORN_LEADER
35 |
36 | [CAMP]
37 | CAN_CAMP:ALWAYS
38 |
39 | [SAFETY]
40 | INTIMIDATED:IS_COMBAT_READY,IS_AWAKE,!IS_DEAD,IS_SAME_SPECIES,@IS_INCAPACITATED,TARGET_IS_COMBAT_READY|IS_COMBAT_READY,IS_AWAKE,!IS_DEAD,!@WEAPON_EQUIPPED_AND_READY,IS_COMBAT_TARGET,TARGET_IS_COMBAT_READY,IS_SAME_SPECIES
41 | DANGER_CLOSE_RANGE:"5"
42 |
43 | [MEMORY]
44 | CACHE_DROP:"investigate_chunk"
45 |
46 | [ITEMS]
47 | GUN:"equip"
48 |
49 | [COMBAT]
50 | SEEK_COMBAT_IF:WEAPON_EQUIPPED_AND_READY
51 | TRUST_LOWER_THAN:"-3"
52 | RANGED_READY:WEAPON_EQUIPPED_AND_READY
53 | MELEE:ALWAYS
54 | MELEE_READY:ALWAYS
55 |
56 | #What target we engage (CLOSEST, HIGHEST, LOWEST)
57 | ENGAGE:"CLOSEST"
58 |
--------------------------------------------------------------------------------
/pyfov.py:
--------------------------------------------------------------------------------
1 | def old_light(los_map, world_pos, size, row, start_slope, end_slope, xx, xy, yx, yy, collision_map, map_size):
2 | _return_chunks = set()
3 |
4 | if start_slope < end_slope:
5 | return los_map, _return_chunks
6 |
7 | x, y, z = world_pos
8 |
9 | _next_start_slope = start_slope
10 |
11 | for i in range(row, size):
12 | _blocked = False
13 |
14 | _d_x = -i
15 | _d_y = -i
16 | while _d_x <= 0:
17 | _l_slope = (_d_x - 0.5) / (_d_y + 0.5)
18 | _r_slope = (_d_x + 0.5) / (_d_y - 0.5)
19 |
20 | if start_slope < _r_slope:
21 | _d_x += 1
22 | continue
23 | elif end_slope>_l_slope:
24 | break
25 |
26 | _sax = _d_x * xx + _d_y * xy
27 | _say = _d_x * yx + _d_y * yy
28 |
29 | if (_sax<0 and abs(_sax)>x) or (_say<0 and abs(_say)>y):
30 | _d_x += 1
31 | continue
32 |
33 | _a_x = x + _sax
34 | _a_y = y + _say
35 |
36 | if _a_x >= map_size[0] or _a_y >= map_size[1]:
37 | _d_x += 1
38 | continue
39 |
40 | _rad2 = size*size
41 | _solid = collision_map[_sax+size, _say+size]
42 |
43 | if (_d_x * _d_x + _d_y * _d_y) < _rad2:
44 | los_map[_sax+size, _say+size] = 1
45 |
46 | if not _solid:
47 | _chunk_key = '%s,%s' % ((_a_x/5)*5, (_a_y/5)*5)
48 |
49 | if not _chunk_key in _return_chunks:
50 | _return_chunks.add(_chunk_key)
51 |
52 | if _blocked:
53 | if _solid:
54 | _next_start_slope = _r_slope
55 | _d_x += 1
56 | continue
57 | else:
58 | _blocked = False
59 | start_slope = _next_start_slope
60 | elif _solid:
61 | _blocked = True
62 | _next_start_slope = _r_slope
63 | _map, _chunk_keys = old_light(los_map, world_pos, size, i+1, start_slope, _l_slope, xx, xy, yx, yy, collision_map, map_size)
64 |
65 | los_map += _map
66 | _return_chunks.update(_chunk_keys)
67 |
68 | _d_x += 1
69 |
70 | if _blocked:
71 | break
72 |
73 | return los_map, _return_chunks
--------------------------------------------------------------------------------
/threads.py:
--------------------------------------------------------------------------------
1 | from globals import MAP_SIZE, MAP_WINDOW_SIZE, WORLD_INFO, SETTINGS, LIFE
2 |
3 | import graphics as gfx
4 |
5 | import bad_numbers
6 | import maps
7 |
8 | import threading
9 | import logging
10 | import time
11 |
12 |
13 | class ChunkHandler(threading.Thread):
14 | def __init__(self, check_every=5):
15 | threading.Thread.__init__(self)
16 |
17 | self.last_checked = -check_every
18 | self.check_every = check_every
19 | self.load_clusters = []
20 |
21 | def check_chunks(self, force=False):
22 | if not force and WORLD_INFO['ticks']-self.last_checked '
7 | sys.exit(1)
8 |
9 | X_SIZE = int(sys.argv[1])
10 | Y_SIZE = int(sys.argv[2])
11 |
12 | SOURCE_MAP = numpy.zeros((X_SIZE,Y_SIZE))
13 |
14 | #print SOURCE_MAP.shape,X_SIZE,Y_SIZE
15 |
16 | def simulate(MAP):
17 | NEXT_MAP = numpy.zeros((X_SIZE,Y_SIZE))
18 |
19 | _i = 0
20 | while 1:
21 | _i += 1
22 | NEXT_MAP = MAP.copy()
23 |
24 |
25 | for MOD_Y in range(X_SIZE-1):
26 | for MOD_X in range(Y_SIZE-1):
27 | if MAP[MOD_Y,MOD_X] == -1:
28 | continue
29 |
30 | #NEIGHBOR_COUNT = 0
31 | LARGEST_SCORE = MAP[MOD_Y,MOD_X]+.90
32 |
33 | for X_OFFSET in range(-1,2):
34 | for Y_OFFSET in range(-1,2):
35 | x = MOD_X+X_OFFSET
36 | y = MOD_Y+Y_OFFSET
37 |
38 | #print Y_SIZE-1
39 | if 0>x or 0>y or x>=X_SIZE-1 or y>=Y_SIZE-1 or MAP[y,x]<=-1:
40 | continue
41 |
42 | if MAP[y,x] > LARGEST_SCORE:
43 | LARGEST_SCORE = MAP[y,x]
44 |
45 | NEXT_MAP[MOD_Y,MOD_X] = LARGEST_SCORE-(0.95)
46 |
47 | if NEXT_MAP[MOD_Y,MOD_X]<0:
48 | NEXT_MAP[MOD_Y,MOD_X] = 0
49 |
50 | draw_map(NEXT_MAP)
51 |
52 | if numpy.array_equal(MAP,NEXT_MAP):
53 | print 'Took: ',_i
54 | return NEXT_MAP
55 |
56 | MAP = NEXT_MAP.copy()
57 |
58 | def dissolve(MAP):
59 | for MOD_Y in range(Y_SIZE):
60 | for MOD_X in range(X_SIZE):
61 | if MAP[MOD_X,MOD_Y] <= 0:
62 | continue
63 |
64 |
65 |
66 | def draw_map(MAP):
67 | for MOD_Y in range(Y_SIZE):
68 | for MOD_X in range(X_SIZE):
69 | NEIGHBOR_COUNT = 0
70 | LARGEST_SCORE = 0
71 | if MAP[MOD_X,MOD_Y] == -1:
72 | print 'x',
73 | else:
74 | print MAP[MOD_X,MOD_Y],
75 |
76 | print ''
77 |
78 |
79 | SOURCE_MAP[5,5]=8
80 | SOURCE_MAP[5,4]=-1
81 | SOURCE_MAP[5,3]=-1
82 | SOURCE_MAP[6,4]=-1
83 |
84 | for x in range(3):
85 | SOURCE_MAP[7+x,4]=-1
86 |
87 | START_TIME = time.time()
88 | SOURCE_MAP = simulate(SOURCE_MAP)
89 |
90 | print time.time()-START_TIME
91 |
92 | draw_map(SOURCE_MAP)
93 |
--------------------------------------------------------------------------------
/contexts.py:
--------------------------------------------------------------------------------
1 | from globals import SETTINGS, LIFE
2 |
3 | import life as lfe
4 |
5 | import encounters
6 | import graphics
7 | import dialog
8 | import alife
9 | import logic
10 |
11 | import logging
12 |
13 | def _create_context_from_phrase(life, phrase):
14 | _reactions = []
15 |
16 | if phrase['gist'] == 'comply':
17 | _reactions.append({'type': 'say','text': 'I give up!',
18 | 'communicate': 'surrender'})
19 |
20 | if lfe.get_held_items(life, matches=[{'type': 'gun'}]):
21 | _reactions.append({'action': 'action',
22 | 'text': '' % ' '.join(phrase['from']['name'])})
23 |
24 | elif phrase['gist'] == 'demand_drop_item':
25 | _reactions.append({'type': 'action','text': 'Drop the item.',
26 | 'action': {'action': 'dropitem','item': phrase['item']},
27 | 'score': 900,
28 | 'delay': lfe.get_item_access_time(life,phrase['item']),
29 | 'communicate': 'dropped_demanded_item'})
30 |
31 | elif phrase['gist'] == 'dialog':
32 | if not phrase['dialog_id'] in LIFE[SETTINGS['controlling']]['dialogs']:
33 | life['dialogs'].append(phrase['dialog_id'])
34 |
35 | if dialog.get_last_message(phrase['dialog_id'])['text']:
36 | logic.show_event(dialog.get_last_message(phrase['dialog_id'])['text'], life=phrase['from'])
37 |
38 | if dialog.is_turn_to_talk(LIFE[SETTINGS['controlling']], phrase['dialog_id']):
39 | dialog.process(LIFE[SETTINGS['controlling']], phrase['dialog_id'])
40 |
41 | elif phrase['gist'] == 'looks_hostile':
42 | #encounters.create_encounter(life, phrase['from'])
43 | #logic.show_event(
44 | alife.speech.start_dialog(phrase['from'], life['id'], 'encounter')
45 | #else:
46 | # logging.warning('Unhandled player context: %s' % phrase['gist'])
47 |
48 | return _reactions
49 |
50 | def create_context(life, action, timeout_callback=None):
51 | #logging.debug('** Created new context %s **' % action['gist'])
52 |
53 | if 'gist' in action:
54 | _reactions = _create_context_from_phrase(life, action)
55 |
56 | return {'action': 'None',
57 | 'text': 'Nothing here!',
58 | 'items': [],
59 | 'reactions': _reactions,
60 | 'from': action['from'],
61 | 'timeout_callback': timeout_callback,
62 | 'time': 150}
63 |
--------------------------------------------------------------------------------
/tests/fast_render_simple_numpy.py:
--------------------------------------------------------------------------------
1 | #The majority of this code is not mine.
2 | #It was originally used to demonstrate
3 | #fast rendering in libtcod using Numpy.
4 | #I adapted it to do the lighting for
5 | #Reactor 3.
6 |
7 | #Original source:
8 | #http://doryen.eptalys.net/forum/index.php?topic=467.0
9 |
10 | import libtcodpy as libtcod
11 | import os
12 |
13 | try:
14 | from numpy import *
15 | except ImportError:
16 | raise ImportError('----- NumPy must be installed. -----')
17 |
18 | SCREEN_W = 80
19 | SCREEN_H = 50
20 | HALF_W = SCREEN_W / 2
21 | HALF_H = SCREEN_H / 2
22 |
23 | libtcod.console_set_custom_font(os.path.join('arial10x10.png'),
24 | libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD)
25 | libtcod.console_init_root(SCREEN_W, SCREEN_H, 'libtcod sample', False)
26 |
27 | (x, y) = meshgrid(range(SCREEN_W), range(SCREEN_H))
28 |
29 | lights = []
30 | lights.append({'x': 40,'y': 20,'brightness': 4.0})
31 | lights.append({'x': 20,'y': 20,'brightness': 3.0})
32 |
33 | while not libtcod.console_is_window_closed():
34 | key = libtcod.console_check_for_keypress()
35 | if key.vk == libtcod.KEY_ESCAPE: break
36 |
37 | _R = zeros((SCREEN_H,SCREEN_W))
38 | _R = add(_R,255)
39 | _G = zeros((SCREEN_H,SCREEN_W))
40 | _G = add(_G,255)
41 | _B = zeros((SCREEN_H,SCREEN_W))
42 | _B = add(_B,255)
43 | _RB = zeros((SCREEN_H,SCREEN_W))
44 | _GB = zeros((SCREEN_H,SCREEN_W))
45 | _BB = zeros((SCREEN_H,SCREEN_W))
46 | _RB = add(_RB,255)
47 | _GB = add(_GB,255)
48 | _BB = add(_BB,255)
49 | render = zeros((SCREEN_H,SCREEN_W))
50 |
51 | for light in lights:
52 | if lights.index(light) == 0:
53 | light['x'] -= 0.01
54 |
55 | sqr_distance = (x - light['x'])**2 + (y - light['y'])**2
56 |
57 | brightness = light['brightness'] / sqr_distance
58 | brightness = clip(brightness * 255, 0, 255)
59 |
60 | _RB = subtract(_RB,brightness).clip(0,255)
61 | _GB = subtract(_GB,brightness).clip(0,255)
62 | _BB = subtract(_BB,brightness).clip(0,255)
63 |
64 | _R = subtract(_R,_RB).clip(0,255)
65 | _G = subtract(_G,_GB).clip(0,255)
66 | _B = subtract(_B,_BB).clip(0,255)
67 |
68 | libtcod.console_fill_background(0, _R, _G, _B)
69 | libtcod.console_fill_foreground(0, _R, _G, _B)
70 | print libtcod.sys_get_fps()
71 | libtcod.console_flush()
72 |
73 |
--------------------------------------------------------------------------------
/docs/testingnotes.md:
--------------------------------------------------------------------------------
1 | Tracking duplicate weapons is difficult
2 | Wounds don't disappear from medical menu
3 | Item access times should be higher for retrieving items from containers that are full
4 | obviously number of items doesn't matter if the container only holds 4 or 5 of them
5 | Large cotainers (10+ items)
6 | de/highlight_position() to get rid of complete screen refresh
7 | The visible targets list appears to grow larger (pushes debug text down) but shows no names
8 | Might be dead agents
9 | Outposts are too close to player spawn.
10 | Limit on number of duplicate cell types in certain radius
11 | Need multiple zone entry points
12 | Crash on equipping storage item from storage
13 | Some military NPCs aren't reloading
14 |
15 | Alife wants group at night
16 |
17 | Put more space between buildings (maybe on a per-type basis.
18 | Space for stores (parking lot, etc)
19 | Space for houses (small yard, fenced-in backyard
20 |
21 | Message box colors?
22 | Sort inventory (or equip menu) by category?
23 |
24 | Worldgen: item spawns
25 | Having buildings generated on the side of the roads?
26 | If we have to seed this, judge based on the location of the lighest color of the road tiles (BROKEN_*)
27 |
28 | Global radio messages
29 | PDA (press tab?)
30 |
31 | Factor weather into visiblity
32 |
33 | "Scan" mode. Over a number of ticks, a heatmap is generated of the
34 | best places to hide in the surrounding area
35 |
36 | When weapons are dropped, the magazines inside the guns are not disowned.
37 |
38 | healed_by memory to boost leadership?
39 | then "Thanks!"
40 | Changes in relationships after memories
41 | Would make combat affect ALife behavior more
42 |
43 | When rendering items, render the most important one on top (gun -> health kit -> ammo)
44 |
45 | AI: What should I do?
46 |
47 |
48 |
49 | Content:
50 | Lock weapon crates: Use small explosives to open them, etc
51 | It's a "task" to open them. Gives the player something to do
52 |
53 |
54 |
55 | Do not clear orders until group leader allows it
56 | i.e., when the AI hits a guard point it should not be cleared (they should wait there)
57 |
58 |
59 | "Are you armed?"
60 | "Negative"
61 | "Stay behind us, then!"
62 |
63 |
64 |
65 | CALL FOR BACKUP - "hot" areas of map that attrack people
66 |
--------------------------------------------------------------------------------
/alife/alife_combat.py:
--------------------------------------------------------------------------------
1 | from globals import *
2 |
3 | import graphics as gfx
4 | import life as lfe
5 |
6 | import judgement
7 | import bad_numbers
8 | import combat
9 | import speech
10 | import sight
11 | import camps
12 | import brain
13 | import stats
14 | import logic
15 | import jobs
16 |
17 | import logging
18 |
19 |
20 | STATE = 'combat'
21 | TIER = TIER_COMBAT-.4
22 |
23 |
24 | def conditions(life, alife_seen, alife_not_seen, targets_seen, targets_not_seen, source_map):
25 | RETURN_VALUE = STATE_UNCHANGED
26 |
27 | _mode = None
28 | if lfe.execute_raw(life, 'state', 'combat'):
29 | _mode = 'combat'
30 |
31 | if not _mode and lfe.execute_raw(life, 'state', 'hunt'):
32 | _mode = 'hunt'
33 |
34 | if not _mode:
35 | return False
36 |
37 | if not lfe.execute_raw(life, 'combat', 'ranged') and not lfe.execute_raw(life, 'combat', 'melee'):
38 | return False
39 |
40 | if not life['state'] == STATE:
41 | life['state_flags'] = {}
42 | stats.battle_cry(life)
43 |
44 | if gfx.position_is_in_frame(life['pos']) and SETTINGS['controlling']:
45 | _can_see = sight.can_see_position(life, LIFE[SETTINGS['controlling']]['pos'])
46 |
47 | if _can_see:
48 | _knows = brain.knows_alife_by_id(life, SETTINGS['controlling'])
49 |
50 | if _knows and judgement.can_trust(life, SETTINGS['controlling']):
51 | if lfe.ticker(life, 'enter_combat_message', 3, fire=True):
52 | logic.show_event('%s readies up.' % ' '.join(life['name']), life=life)
53 |
54 | RETURN_VALUE = STATE_CHANGE
55 |
56 | brain.flag(life, 'combat_mode', value=_mode)
57 |
58 | return RETURN_VALUE
59 |
60 | def ranged_attack(life):
61 | _all_targets = judgement.get_threats(life, ignore_escaped=False)
62 |
63 | combat.ranged_combat(life, _all_targets)
64 |
65 | def melee_attack(life):
66 | _all_targets = judgement.get_threats(life)
67 |
68 | combat.melee_combat(life, _all_targets)
69 |
70 | def tick(life, alife_seen, alife_not_seen, targets_seen, targets_not_seen, source_map):
71 | _all_targets = judgement.get_threats(life, ignore_escaped=1)
72 |
73 | if lfe.execute_raw(life, 'combat', 'ranged_ready', break_on_true=True, break_on_false=False):
74 | combat.ranged_combat(life, _all_targets)
75 |
76 | if lfe.execute_raw(life, 'combat', 'melee_ready', break_on_true=True, break_on_false=False):
77 | combat.melee_combat(life, _all_targets)
--------------------------------------------------------------------------------
/docs/style_guide.md:
--------------------------------------------------------------------------------
1 | Programming Style Guide
2 | -----------------------
3 | A quick note: Throughout development my programming style changed somewhat, so you will see code that does not follow the rules below. All new code obeys these rules.
4 |
5 | **This is not a primer on writing effective Python. I am also not a World Famous Superstar Master Python Coder.**
6 |
7 | * Use tabs for line indents. Spaces can be used AFTER tabs to help improve readability or for stylistic reasons if needed.
8 | * Variable names should be prefixed with an underscore (`_chunk_key`) if they are defined outside of a `for` loop. Variables used to iterate through lists/are created in the for loop's definition should omit the underscore. Arguments should never start with an underscore unless keyword arguments (`kwargs`) are expected to clash with them.
9 |
10 | Abusing Lambdas
11 | -------------
12 | Throughout the code you'll see opportunites to filter results of certain functions by passing a function to keyword argument `filter_if`. However, `filter_if` is usually only called with one argument, rendering the majority of possible filters that need extra arguments completely useless. Instead of filtering the results after they are returned, we can pack the variables we need to access later inside the lambda's definition.
13 |
14 | def manage_combat(life, group_id):
15 | if get_stage(life, group_id) == STAGE_RAIDING:
16 | prepare_for_raid(life, group_id)
17 | return False
18 |
19 | for known_group_id in life['known_groups']:
20 | if group_id == known_group_id:
21 | continue
22 |
23 | if not get_group_memory(life, known_group_id, 'alignment') == 'hostile':
24 | announce(life, group_id, 'inform_of_known_group', group_id=known_group_id,
25 | filter_if=lambda alife: group_exists(alife, known_group_id))
26 |
27 | _known_group_members = get_group_memory(life, known_group_id, 'members')
28 |
29 | In the above example we can see that `known_group_id` is created in the local scope, but can still be referenced and used inside of the lambda when it is called later. Another example, this time showing an entire `life` structure being passed:
30 |
31 | announce(life, group_id, 'combat_ready', ignore_if_said_in_last=1000, filter_if=lambda alife: brain.get_alife_flag(life, alife['id'], 'combat_ready'))
32 |
33 | Note that the lambda's argument was renamed to `alife` to prevent conflicts with `life`.
34 |
--------------------------------------------------------------------------------
/scripting.py:
--------------------------------------------------------------------------------
1 | #Command
2 | # CREATE_ITEM(- , )
3 | # DELETE()
4 |
5 | from globals import *
6 |
7 | import graphics as gfx
8 |
9 | import effects
10 | import items
11 | import life
12 |
13 | import logging
14 | import re
15 |
16 | def parse_console(text):
17 | return text.replace('pc', 'SETTINGS[\'controlling\']')
18 |
19 | def execute(script, **kvargs):
20 | for function in script:
21 | _args = parse_arguments(script[function], **kvargs)
22 |
23 | if function == 'CREATE_AND_OWN_ITEM':
24 | _i = items.create_item(_args[0], position=_args[1])
25 | life.add_item_to_inventory(kvargs['owner'], _i)
26 | elif function == 'DELETE':
27 | items.delete_item(ITEMS[kvargs['item_uid']])
28 | elif function == 'LIGHT_FOLLOW':
29 | _item = ITEMS[kvargs['item_uid']]
30 |
31 | effects.create_light(items.get_pos(kvargs['item_uid']),
32 | (255, 255, 255),
33 | _item['brightness'],
34 | _item['light_shake'],
35 | follow_item=kvargs['item_uid'])
36 | elif function == 'LIGHT_FOLLOW_REMOVE':
37 | _item = ITEMS[kvargs['item_uid']]
38 |
39 | effects.delete_light_at(items.get_pos(kvargs['item_uid']))
40 | elif function == 'TOGGLE_BLOCK':
41 | _item = ITEMS[kvargs['item_uid']]
42 |
43 | if _item['blocking']:
44 | _item['blocking'] = False
45 | else:
46 | _item['blocking'] = True
47 | else:
48 | logging.error('Script: \'%s\' is not a valid function.' % function)
49 |
50 | def initiate(owner, text):
51 | _functions = get_functions(owner, text)
52 |
53 | return _functions
54 |
55 | def parse_arguments(arguments, **kvargs):
56 | _returned_arguments = []
57 | for arg in [arg.strip().rstrip(')') for arg in arguments.split(',')]:
58 | _returned_arguments.append(parse_argument(ITEMS[kvargs['item_uid']], arg))
59 |
60 | return _returned_arguments
61 |
62 | def parse_argument(owner, argument):
63 | for match in re.findall('(self.[a-zA-Z_]*)', argument):
64 | _value = match.split('.')[1]
65 |
66 | if not _value in owner:
67 | logging.error('Script syntax: \'%s\' not found in self.' % _value)
68 | return None
69 |
70 | return owner[_value]
71 |
72 | return argument
73 |
74 | def get_functions(owner, text):
75 | _functions = {}
76 |
77 | for func in text.split(':'):
78 | for function in re.findall('[a-zA-Z_]*\(.*\)', func):
79 | _name,_args = function.split('(')
80 | _functions[_name] = _args#parse_arguments(owner, _args)
81 |
82 | return _functions
83 |
--------------------------------------------------------------------------------
/maputils.py:
--------------------------------------------------------------------------------
1 | from globals import *
2 | from tiles import *
3 |
4 | import zones
5 | import maps
6 |
7 | import random
8 | import copy
9 | import sys
10 |
11 | def get_map_size(map):
12 | return (len(map),len(map[0]),len(map[0][0]))
13 |
14 | def resize_map(map,size):
15 | _old_size = get_map_size(map)
16 |
17 | if _old_size[0]>size[0] or _old_size[1]>size[1] or _old_size[2]>size[2]:
18 | print 'Warning: Attempting to shink the map! Data will be lost.'
19 |
20 | _new_map = copy.deepcopy(map)
21 |
22 | if _old_size[0]>size[0]:
23 | for x in range(abs(_old_size[0]-size[0])):
24 | _new_map.pop()
25 | elif _old_size[0]size[1]:
44 | for x in range(size[0]):
45 | for y in range(abs(_old_size[1]-size[1])):
46 | _new_map[x].pop()
47 | elif _old_size[1]size[2]:
64 | for x1 in range(_old_size[0]):
65 | for y1 in range(_old_size[1]):
66 | for z in range(abs(_old_size[2]-size[2])):
67 | _new_map[x1][y1].pop()
68 | elif _old_size[2]+f - Appear Friendly')
39 | _text.append('+h - Appear Hostile')
40 | #_text.append('+s - Surrender')
41 | #_text.append('+q - Ignore')
42 | _text.append('_' * 38)
43 |
44 | _encounter['text'] = _text
45 | _encounter['start_time'] = WORLD_INFO['ticks']
46 |
47 | SETTINGS['following'] = target['id']
48 | life['encounters'].append(_encounter)
49 | logging.debug('%s created encounter.' % ' '.join(target['name']))
50 | SETTINGS['encounter animation timer'] = ENCOUNTER_ANIMATION_TIME
51 |
52 | return _encounter
53 |
54 | def draw_encounter(life, encounter):
55 | if SETTINGS['encounter animation timer']>0:
56 | if SETTINGS['encounter animation timer'] == ENCOUNTER_ANIMATION_TIME:
57 | lfe.set_animation(encounter['target'], TICKER, speed=1, loops=2)
58 |
59 | SETTINGS['encounter animation timer']-=1
60 | return False
61 |
62 | if not 'console' in encounter:
63 | encounter['console'] = tcod.console_new(40, 40)
64 |
65 | _y = 1
66 | for line in encounter['text']:
67 | _x = 1
68 |
69 | while line:
70 | _line = line
71 | while len(_line)>=40:
72 | _words = _line.split(' ')
73 | _line = ' '.join(_words[:len(_words)-1])
74 |
75 | _lines = [_line]
76 |
77 | if not _line == line:
78 | _lines.append(line.replace(_line, ''))
79 |
80 | _i = 0
81 | for txt in _lines:
82 | tcod.console_print(encounter['console'],
83 | _x+_i,
84 | _y,
85 | txt)
86 |
87 | _i += 1
88 | _y += 1
89 |
90 | _x += 1
91 | _y += 1
92 |
93 | break
94 |
--------------------------------------------------------------------------------
/events.py:
--------------------------------------------------------------------------------
1 | from globals import LIFE
2 |
3 | import alife
4 |
5 | import logging
6 |
7 | def create(name, process_callback, process_arguments, complete_callback=None, complete_arguments=None, fail_callback=None, fail_arguments=None, repeat_every=-1):
8 | _event = {'name': name,
9 | 'process': {'callback': process_callback, 'arguments': process_arguments},
10 | 'complete_on': {'callback': complete_callback, 'arguments': complete_arguments},
11 | 'fail_on': {'callback': fail_callback, 'arguments': fail_arguments},
12 | 'repeat': repeat_every,
13 | 'completed': False,
14 | 'failed': False,
15 | 'accepted': [],
16 | 'flags': {}}
17 |
18 | return _event
19 |
20 | def process_event(event):
21 | event['failed'] = False
22 |
23 | if event['completed']:
24 | return True
25 |
26 | if event['complete_on']['callback'] and event['complete_on']['callback'](**event['complete_on']['arguments']):
27 | event['completed'] = True
28 | return True
29 |
30 | if event['fail_on']['callback']:
31 | _args = {}
32 |
33 | for key in event['fail_on']['arguments']:
34 | if isinstance(event['fail_on']['arguments'][key], dict) and '_action' in event['fail_on']['arguments'][key]:
35 | _args[key] = alife.action.execute(event['fail_on']['arguments'][key])
36 | else:
37 | _args[key] = event['fail_on']['arguments'][key]
38 |
39 | if not alife.action.execute(event['fail_on']['callback'])(**_args):
40 | event['failed'] = True
41 | return False
42 |
43 | if event['process']:
44 | _args = {}
45 | for key in event['process']['arguments']:
46 | if isinstance(event['process']['arguments'][key], dict) and '_action' in event['process']['arguments'][key]:
47 | _args[key] = alife.action.execute(event['process']['arguments'][key])
48 | else:
49 | _args[key] = event['process']['arguments'][key]
50 |
51 | alife.action.execute(event['process']['callback'])(**_args)
52 |
53 | return True
54 |
55 | def accept(event, life_id):
56 | if life_id in event['accepted']:
57 | return False
58 |
59 | event['accepted'].append(life_id)
60 |
61 | logging.debug('%s has accepted event: %s' % (' '.join(LIFE[life_id]['name']), event['name']))
62 |
63 | def has_accepted(event, life_id):
64 | if life_id in event['accepted']:
65 | return True
66 |
67 | return False
68 |
69 | def clear_accepted(event):
70 | event['accepted'] = []
71 |
72 | logging.debug('Accepted list cleared for event: %s' % event['name'])
73 |
74 | def flag(event, flag, value):
75 | _event['flags'][flag] = value
76 |
77 | def unflag(event, flag):
78 | del _event['flags'][flag]
79 |
80 | def get_flag(event, flag):
81 | if flag in _event['flags']:
82 | return _event['flags'][flag]
83 |
84 | return False
--------------------------------------------------------------------------------
/artifacts.py:
--------------------------------------------------------------------------------
1 | from globals import WORLD_INFO, MAP_SIZE, SETTINGS, LIFE
2 | from alife import factions
3 |
4 | import graphics as gfx
5 |
6 | import effects
7 | import bad_numbers
8 | import timers
9 | import items
10 | import life
11 |
12 | import random
13 |
14 |
15 | def find_territory(has_owner=False, y_min=0):
16 | _territories = []
17 |
18 | for territory_id in WORLD_INFO['territories']:
19 | _territory = WORLD_INFO['territories'][territory_id]
20 | _chunk_key = random.choice(_territory['chunk_keys'])
21 |
22 | if WORLD_INFO['chunk_map'][_chunk_key]['pos'][1]/float(MAP_SIZE[1])Night Terror
2 | Night Terror
3 | mutant
4 |
5 | LEGS[blleg,brleg]|CAN_GROUP|CAN_SEE|HUNGER|THIRST|MELEE[jaw,flpaw,frpaw]
6 | vision_max=20|hunger_max=20000|thirst_max=10000|hunger=hunger_max|thirst=thirst_max
7 | T
8 |
9 |
10 |
11 | SKIN|BONE|CRUCIAL|CAN_HOLD|CAN_BITE
12 | 1
13 | 1.5
14 | 2
15 |
16 |
17 |
18 | SKIN|BONE|CRUCIAL|AFFECTS[sight,smell,hearing]
19 | jaw
20 | 2
21 | 1.5
22 | 4
23 |
24 |
25 |
26 | SKIN|MUSCLE|BONE|CRUCIAL
27 | head
28 | 2
29 | 2
30 | 3
31 |
32 |
33 |
34 | SKIN|MUSCLE|BONE|CRUCIAL
35 | neck
36 | 2
37 | 1.5
38 | 2
39 |
40 |
41 |
42 | SKIN|MUSCLE|BONE
43 | chest
44 | 1
45 | 0.5
46 | 2
47 |
48 |
49 |
50 | SKIN|MUSCLE|BONE
51 | chest
52 | 1
53 | 0.5
54 | 2
55 |
56 |
57 |
58 | SKIN|MUSCLE|BONE|SHARP
59 | flleg
60 | 1
61 | 0.5
62 | 1
63 |
64 |
65 |
66 | SKIN|MUSCLE|BONE|SHARP
67 | frleg
68 | 1
69 | 0.5
70 | 1
71 |
72 |
73 |
74 | SKIN|MUSCLE|BONE|CRUCIAL
75 | chest
76 | 1
77 | 1.5
78 | 4
79 |
80 |
81 |
82 | SKIN|MUSCLE|BONE|CRUCIAL
83 | stomach
84 | 2
85 | 1
86 | 3
87 |
88 |
89 |
90 | SKIN|MUSCLE|BONE
91 | hip
92 | 1
93 | 1.3
94 | 2
95 |
96 |
97 |
98 | SKIN|MUSCLE|BONE
99 | hip
100 | 1
101 | 1.3
102 | 2
103 |
104 |
105 |
106 | SKIN|MUSCLE|BONE|SHARP
107 | blleg
108 | 1
109 | 0.5
110 | 1
111 |
112 |
113 |
114 | SKIN|MUSCLE|BONE|SHARP
115 | brleg
116 | 1
117 | 0.5
118 | 1
119 |
120 |
121 |
--------------------------------------------------------------------------------
/tools/guntest.py:
--------------------------------------------------------------------------------
1 | import random
2 | import numpy
3 | import json
4 | import math
5 | import sys
6 |
7 |
8 | if len(sys.argv) == 1:
9 | sys.exit(1)
10 |
11 |
12 | _weapon = {'recoil': 0.3,
13 | 'accuracy': 0.9}
14 | stance_mod = 1.0
15 | aim_difficulty = 1.8
16 | firearms_skill_mod = 0.55
17 | hit_certainty_mod = 0.6
18 | bullets = 10
19 | simulaton_ticks = 8
20 |
21 | def load_weapon(weapon_file):
22 | with open(weapon_file, 'r') as wfile:
23 | _weapon.update(json.loads(''.join(wfile.readlines())))
24 |
25 | def velocity(direction, speed):
26 | rad = direction*(math.pi/180)
27 | velocity = numpy.multiply(numpy.array([math.cos(rad), math.sin(rad)]), speed)
28 |
29 | return [velocity[0], -velocity[1], 0]
30 |
31 | def clip(number,start,end):
32 | return max(start, min(number, end))
33 |
34 | def bullet_trajectory(bullet_pos, bullet_direction, ticks=simulaton_ticks):
35 | bullet_velocity = velocity(bullet_direction, 5)
36 |
37 | for i in range(ticks):
38 | bullet_pos[0] += bullet_velocity[0]
39 | bullet_pos[1] += bullet_velocity[1]
40 |
41 | return bullet_pos
42 |
43 | def simulate(firearms_skill, recoil=0.0):
44 | recoil = float(recoil)
45 | bullet_direction = 0
46 | bullet_pos = [0, 0]
47 | bullet_velocity = [0, 0]
48 | _deviations = []
49 | _hits = 0
50 |
51 | for i in range(bullets):
52 | bullet_velocity = [0, 0]
53 | bullet_pos = [0, 0]
54 | bullet_deviation = (1-_weapon['accuracy'])+recoil
55 | deviation_mod = aim_difficulty*(1-((firearms_skill/10.0)*firearms_skill_mod))
56 | deviation = (bullet_deviation*aim_difficulty)*deviation_mod
57 | bullet_direction = random.uniform(-deviation, deviation)
58 |
59 | ########################
60 | ## ENABLE FOR RELEASE ##
61 | ########################
62 | #recoil = clip(recoil+(_weapon['recoil']*stance_mod), 0.0, 1.0)
63 |
64 | _end_pos = bullet_trajectory(bullet_pos, bullet_direction)
65 | _direction_deviation = abs(bullet_direction)
66 | _target_hit_certainty = _direction_deviation/hit_certainty_mod
67 | _hits += _target_hit_certainty<=1
68 | _deviations.append(_direction_deviation)
69 |
70 | #print 'Deviations: mean=%0.4f, min=%0.4f, max=%0.4f' % (sum(_deviations)/len(_deviations), min(_deviations), max(_deviations))
71 | #print 'Accuracy:', _hits/float(bullets), '\n'
72 |
73 | #print 'Trajectory deviation: %0.4f' % _direction_deviation
74 | #print 'Non-certainty: %s (hit=%s)' % (_target_hit_certainty, _target_hit_certainty<=1)
75 | #print 'Limb accuracy: %0.4f' % (1-(_target_hit_certainty/1.0))*int(_target_hit_certainty<=1)
76 | #print
77 | #print 'dir', bullet_direction, 'dev', bullet_deviation, 'rec', recoil
78 |
79 | return _hits/float(bullets), _deviations
80 |
81 | _path = sys.argv[1]
82 | load_weapon(_path)
83 |
84 | _accuracies = []
85 |
86 | for i in range(1, 10+1):
87 | print 'Skill: %s\t' % i
88 |
89 | for r in range(0, 5):
90 | recoil = r/5.0
91 | _accuracy, _deviations = simulate(i, recoil=recoil)
92 | _accuracies.append(_accuracy)
93 |
94 | print '\trecoil=%0.4f, accuracy=%0.4f, avg. deviation=%0.4f' % (recoil, _accuracy, sum(_deviations)/float(len(_deviations)))
95 |
96 | print _weapon['name'], 'accuracy:', sum(_accuracies)/float(len(_accuracies))
97 |
--------------------------------------------------------------------------------
/render_fast_los.pyx:
--------------------------------------------------------------------------------
1 | from globals import *
2 |
3 | import render_los
4 | import numbers
5 | import numpy
6 | import time
7 | import math
8 |
9 | import cython
10 |
11 | cpdef int clip(int number, int start, int end):
12 | return max(start, min(number, end))
13 |
14 | @cython.locals(direction=cython.int, speed=cython.int)
15 | def velocity(direction,speed):
16 | cdef double rad = direction*(math.pi/180)
17 | velocity = numpy.multiply(numpy.array([math.cos(rad),math.sin(rad)]),speed)
18 |
19 | return [velocity[0],-velocity[1],0]
20 |
21 | @cython.locals(sight=cython.int, intensity=cython.int)
22 | def check_dirs(at, sight, source_map, los, intensity=45, already_checked={}, scan=(0, 360), quad_check=True, no_edge=False):
23 | cdef int deg, _i, _x, _y, __x, __y, _wall, _end_x, _end_y, end_point[2], start_point[3], _top_left[3]
24 | cdef int X_MAP_WINDOW_SIZE = MAP_WINDOW_SIZE[0]
25 | cdef int Y_MAP_WINDOW_SIZE = MAP_WINDOW_SIZE[1]
26 | cdef int X_MAP_SIZE = MAP_SIZE[0]
27 | cdef int Y_MAP_SIZE = MAP_SIZE[1]
28 |
29 | _check_dirs = already_checked
30 | _checked_quads = [deg/90 for deg in already_checked]
31 | start_point[0] = at[0]
32 | start_point[1] = at[1]
33 | start_point[2] = at[2]
34 |
35 | for deg in range(scan[0], scan[1], intensity):
36 | if quad_check and deg/90 in _checked_quads:
37 | continue
38 |
39 | _end_x,_end_y = velocity(deg, sight)[:2]
40 | end_point[0] = start_point[0]+int(round(_end_x))
41 | end_point[1] = start_point[1]+int(round(_end_y))
42 | _line = render_los.draw_line(start_point[0], start_point[1], end_point[0], end_point[1])
43 | _i = 0
44 | _wall = 0
45 | __x = clip(start_point[0]-(los.shape[1]/2),0,X_MAP_SIZE-(los.shape[1]/2))
46 | __y = clip(start_point[1]-(los.shape[0]/2),0,Y_MAP_SIZE-(los.shape[0]/2))
47 |
48 | for pos in _line:
49 | _x,_y = pos
50 | _x -= __x
51 | _y -= __y
52 | _i += 1
53 |
54 | if _x<0 or _x>=los.shape[1]-1 or _y<0 or _y>=los.shape[0]-1 or pos[0]>=MAP_SIZE[0]-1 or pos[1]>=MAP_SIZE[1]-1:
55 | continue
56 |
57 | if source_map[pos[0]][pos[1]][start_point[2]+1]:
58 | _check_dirs[deg] = _line[:_i]
59 | if not _wall:
60 | _wall = 1
61 |
62 | if not no_edge:
63 | los[_y, _x] = 0
64 |
65 | continue
66 |
67 | if _wall:
68 | los[_y, _x] = 0
69 |
70 | return _check_dirs
71 |
72 | def render_fast_los(at, sight_length, source_map, no_edge=False):
73 | _stime = time.time()
74 | cdef int sight = sight_length
75 | cdef int intensity = 45
76 | cdef int quad
77 | los = numpy.ones((sight, sight))
78 |
79 | _check_dirs = {}
80 | while 1:
81 | _check_dirs = check_dirs(at, sight, source_map, los, intensity=intensity, already_checked=_check_dirs, no_edge=no_edge)
82 | quads_to_check = []
83 |
84 | for deg in [entry/90 for entry in _check_dirs if _check_dirs[entry]]:
85 | if not deg in quads_to_check:
86 | quads_to_check.append(deg)
87 |
88 | intensity /= 2
89 |
90 | if intensity<=6:
91 | break
92 |
93 | _cover = {'pos': None,'score':9000}
94 | for quad in quads_to_check:
95 | _scan = scan=(numbers.clip(quad*90, 0, 360), (numbers.clip((quad+1)*90, 0, 360)))
96 | _check_dirs = check_dirs(at, sight, source_map, los, intensity=1, scan=_scan, quad_check=False, no_edge=no_edge)
97 |
98 | return los
99 |
--------------------------------------------------------------------------------
/data/life/dog.json:
--------------------------------------------------------------------------------
1 | {
2 | "body": {
3 | "hip": {
4 | "damage_mod": 2.0,
5 | "bleed_mod": 1.0,
6 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE|CRUCIAL",
7 | "parent": "stomach",
8 | "size": 3
9 | },
10 | "frleg": {
11 | "damage_mod": 1.0,
12 | "bleed_mod": 0.5,
13 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE",
14 | "parent": "chest",
15 | "size": 2
16 | },
17 | "head": {
18 | "damage_mod": 2.0,
19 | "bleed_mod": 1.5,
20 | "flags": "SKIN[{\"thickness\": 2}]|BONE|CRUCIAL|AFFECTS[sight,smell,hearing]",
21 | "parent": "jaw",
22 | "size": 4
23 | },
24 | "stomach": {
25 | "damage_mod": 1.0,
26 | "bleed_mod": 1.5,
27 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE|CRUCIAL",
28 | "parent": "chest",
29 | "size": 4
30 | },
31 | "neck": {
32 | "damage_mod": 2.0,
33 | "bleed_mod": 2.0,
34 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE|CRUCIAL",
35 | "parent": "head",
36 | "size": 3
37 | },
38 | "frpaw": {
39 | "damage_mod": 1.0,
40 | "bleed_mod": 0.5,
41 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE|SHARP",
42 | "parent": "frleg",
43 | "size": 1
44 | },
45 | "jaw": {
46 | "damage_mod": 1.0,
47 | "bleed_mod": 1.5,
48 | "flags": "SKIN[{\"thickness\": 2}]|BONE|CRUCIAL|CAN_HOLD|CAN_BITE",
49 | "size": 2
50 | },
51 | "flpaw": {
52 | "damage_mod": 1.0,
53 | "bleed_mod": 0.5,
54 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE|SHARP",
55 | "parent": "flleg",
56 | "size": 1
57 | },
58 | "chest": {
59 | "damage_mod": 2.0,
60 | "bleed_mod": 1.5,
61 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE|CRUCIAL",
62 | "parent": "neck",
63 | "size": 2
64 | },
65 | "blpaw": {
66 | "damage_mod": 1.0,
67 | "bleed_mod": 0.5,
68 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE|SHARP",
69 | "parent": "blleg",
70 | "size": 1
71 | },
72 | "flleg": {
73 | "damage_mod": 1.0,
74 | "bleed_mod": 0.5,
75 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE",
76 | "parent": "chest",
77 | "size": 2
78 | },
79 | "blleg": {
80 | "damage_mod": 1.0,
81 | "bleed_mod": 1.3,
82 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE",
83 | "parent": "hip",
84 | "size": 2
85 | },
86 | "brleg": {
87 | "damage_mod": 1.0,
88 | "bleed_mod": 1.3,
89 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE",
90 | "parent": "hip",
91 | "size": 2
92 | },
93 | "brpaw": {
94 | "damage_mod": 1.0,
95 | "bleed_mod": 0.5,
96 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE|SHARP",
97 | "parent": "brleg",
98 | "size": 1
99 | }
100 | },
101 | "name": "Wild Dog",
102 | "vars": "vision_max=35|hunger_max=20000|thirst_max=10000|hunger=hunger_max|thirst=thirst_max",
103 | "flags": "LEGS[flleg,frleg,blleg,brleg]|CAN_GROUP|CAN_SEE|HUNGER|THIRST|MELEE[jaw,flpaw,frpaw]",
104 | "type": "animal",
105 | "species": "dog",
106 | "icon": "D"
107 | }
--------------------------------------------------------------------------------
/inputs.py:
--------------------------------------------------------------------------------
1 | from cStringIO import StringIO
2 | from globals import *
3 |
4 | from debug import *
5 |
6 | import libtcodpy as tcod
7 | import graphics as gfx
8 |
9 | import scripting
10 | import menus
11 |
12 | import sys
13 |
14 | def reset_input():
15 | for key in INPUT:
16 | INPUT[key] = False
17 |
18 | def get_input():
19 | tcod.sys_check_for_event(tcod.EVENT_ANY, KEY, MOUSE)
20 | reset_input()
21 | get_keyboard_input()
22 | get_mouse_input()
23 |
24 | def get_keyboard_input():
25 | global KEYBOARD_STRING
26 |
27 | if not KEY.vk:
28 | return False
29 |
30 | if KEY.c:
31 | _key = chr(KEY.c)
32 | else:
33 | if KEY.pressed:
34 | if KEY.vk == tcod.KEY_RIGHT:
35 | INPUT['right'] = True
36 | elif KEY.vk == tcod.KEY_LEFT:
37 | INPUT['left'] = True
38 | elif KEY.vk == tcod.KEY_DOWN:
39 | INPUT['down'] = True
40 | elif KEY.vk == tcod.KEY_UP:
41 | INPUT['up'] = True
42 |
43 | return True
44 |
45 | if SETTINGS['draw console']:
46 | if KEY.vk == tcod.KEY_ENTER and len(KEYBOARD_STRING[0]):
47 | #Taken from: http://stackoverflow.com/a/3906309
48 | old_stdout = sys.stdout
49 | redirected_output = sys.stdout = StringIO()
50 | exec(scripting.parse_console(KEYBOARD_STRING[0].rstrip()))
51 | sys.stdout = old_stdout
52 |
53 | gfx.log('>'+KEYBOARD_STRING[0].rstrip())
54 | gfx.log(' '+redirected_output.getvalue())
55 |
56 | KEYBOARD_STRING[0] = ''
57 |
58 | elif KEY.vk == tcod.KEY_BACKSPACE:
59 | KEYBOARD_STRING[0] = KEYBOARD_STRING[0][:len(KEYBOARD_STRING[0])-1]
60 |
61 | if not ACTIVE_MENU['menu'] == -1:
62 | _item = menus.is_getting_input(ACTIVE_MENU['menu'])
63 | if _item and KEY.pressed:
64 | _item['values'][0] += _key
65 |
66 | if not INPUT.has_key(_key):
67 | INPUT[_key] = False
68 |
69 | if not INPUT[_key] and KEY.pressed:
70 | if SETTINGS['draw console']:
71 | KEYBOARD_STRING[0] += _key
72 |
73 | INPUT[_key] = True
74 | else:
75 | #if INPUT[_key]:
76 | # #TODO: A 'true' release...?
77 | # pass
78 |
79 | INPUT[_key] = False
80 |
81 | def set_mouse_click_callback(button, function):
82 | if button == 1:
83 | MOUSE_CALLBACKS['m1_click'] = function
84 | else:
85 | MOUSE_CALLBACKS['m2_click'] = function
86 |
87 | def set_mouse_move_callback(function):
88 | MOUSE_CALLBACKS['move'] = function
89 |
90 | def get_mouse_location():
91 | return CAMERA_POS[0]+MOUSE_POS[0], CAMERA_POS[1]+MOUSE_POS[1]
92 |
93 | def get_mouse_input():
94 | #TODO: I can't get mouse input to work properly...
95 | _mouse = tcod.mouse_get_status()
96 | _old_x, _old_y = MOUSE_POS
97 |
98 | MOUSE_POS[0] = _mouse.cx
99 | MOUSE_POS[1] = _mouse.cy
100 |
101 | if not [_old_x, _old_y] == MOUSE_POS:
102 | if MOUSE_CALLBACKS['move']:
103 | MOUSE_CALLBACKS['move']()
104 |
105 | if not INPUT['m1'] and _mouse.lbutton_pressed:
106 | if MOUSE_CALLBACKS['m1_click']:
107 | MOUSE_CALLBACKS['m1_click']()
108 |
109 | INPUT['m1'] = True
110 | else:
111 | INPUT['m1'] = False
112 |
113 | if not INPUT['m2'] and _mouse.rbutton_pressed:
114 | if MOUSE_CALLBACKS['m2_click']:
115 | MOUSE_CALLBACKS['m2_click']()
116 |
117 | INPUT['m2'] = True
118 | else:
119 | INPUT['m2'] = False
--------------------------------------------------------------------------------
/data/life/dog.xml:
--------------------------------------------------------------------------------
1 | Wild Dog
2 | dog
3 | animal
4 |
5 | LEGS[flleg,frleg,blleg,brleg]|CAN_GROUP|CAN_SEE|HUNGER|THIRST|MELEE[jaw,flpaw,frpaw]
6 | vision_max=35|hunger_max=20000|thirst_max=10000|hunger=hunger_max|thirst=thirst_max
7 | D
8 |
9 |
10 |
11 | SKIN[{"thickness": 2}]|BONE|CRUCIAL|CAN_HOLD|CAN_BITE
12 | 1
13 | 1.5
14 | 2
15 |
16 |
17 |
18 | SKIN[{"thickness": 2}]|BONE|CRUCIAL|AFFECTS[sight,smell,hearing]
19 | jaw
20 | 2
21 | 1.5
22 | 4
23 |
24 |
25 |
26 | SKIN[{"thickness": 2}]|MUSCLE|BONE|CRUCIAL
27 | head
28 | 2
29 | 2
30 | 3
31 |
32 |
33 |
34 | SKIN[{"thickness": 2}]|MUSCLE|BONE|CRUCIAL
35 | neck
36 | 2
37 | 1.5
38 | 2
39 |
40 |
41 |
42 | SKIN[{"thickness": 2}]|MUSCLE|BONE
43 | chest
44 | 1
45 | 0.5
46 | 2
47 |
48 |
49 |
50 | SKIN[{"thickness": 2}]|MUSCLE|BONE
51 | chest
52 | 1
53 | 0.5
54 | 2
55 |
56 |
57 |
58 | SKIN[{"thickness": 2}]|MUSCLE|BONE|SHARP
59 | flleg
60 | 1
61 | 0.5
62 | 1
63 |
64 |
65 |
66 | SKIN[{"thickness": 2}]|MUSCLE|BONE|SHARP
67 | frleg
68 | 1
69 | 0.5
70 | 1
71 |
72 |
73 |
74 | SKIN[{"thickness": 2}]|MUSCLE|BONE|CRUCIAL
75 | chest
76 | 1
77 | 1.5
78 | 4
79 |
80 |
81 |
82 | SKIN[{"thickness": 2}]|MUSCLE|BONE|CRUCIAL
83 | stomach
84 | 2
85 | 1
86 | 3
87 |
88 |
89 |
90 | SKIN[{"thickness": 2}]|MUSCLE|BONE
91 | hip
92 | 1
93 | 1.3
94 | 2
95 |
96 |
97 |
98 | SKIN[{"thickness": 2}]|MUSCLE|BONE
99 | hip
100 | 1
101 | 1.3
102 | 2
103 |
104 |
105 |
106 | SKIN[{"thickness": 2}]|MUSCLE|BONE|SHARP
107 | blleg
108 | 1
109 | 0.5
110 | 1
111 |
112 |
113 |
114 | SKIN[{"thickness": 2}]|MUSCLE|BONE|SHARP
115 | brleg
116 | 1
117 | 0.5
118 | 1
119 |
120 |
121 |
--------------------------------------------------------------------------------
/alife/noise.py:
--------------------------------------------------------------------------------
1 | #This isn't for playing sound - R3 uses outside libraries for that,
2 | #and this module utilizes that in places.
3 | #
4 | #This is for simulating noises (that aren't voices)
5 |
6 | from globals import *
7 |
8 | import graphics as gfx
9 |
10 | import language
11 | import judgement
12 | import bad_numbers
13 | import sight
14 | import brain
15 |
16 | import logging
17 | import random
18 |
19 | FAR_TEXT = ['You hear @t to the @d.']
20 |
21 | def create(position, volume, close_text, far_text, skip_on_visual=True, **sound):
22 | _noise = {'pos': position,
23 | 'volume': volume,
24 | 'text': (close_text, far_text),
25 | 'skip_on_visual': skip_on_visual}
26 | _noise.update(sound)
27 |
28 | _spread(_noise)
29 |
30 | def update_targets_around_noise(life, noise):
31 | _most_likely_target = {'target': None, 'last_seen_time': 0}
32 |
33 | if 'target' in noise and not life['id'] == noise['target']:
34 | _visiblity = bad_numbers.clip(sight.get_stealth_coverage(LIFE[noise['target']]), 0.0, 1.0)
35 | _visiblity = bad_numbers.clip(_visiblity+(bad_numbers.distance(life['pos'], LIFE[noise['target']]['pos']))/(sight.get_vision(life)/2), 0, 1.0)
36 |
37 | if _visiblity >= sight.get_visiblity_of_position(life, LIFE[noise['target']]['pos']):
38 | brain.meet_alife(life, LIFE[noise['target']])
39 |
40 | life['know'][noise['target']]['escaped'] = 1
41 | life['know'][noise['target']]['last_seen_at'] = noise['pos'][:]
42 | life['know'][noise['target']]['last_seen_time'] = 0
43 |
44 | for target in life['know'].values():
45 | if not target['escaped'] or not target['last_seen_at'] or target['dead']:
46 | continue
47 |
48 | if bad_numbers.distance(target['last_seen_at'], noise['pos']) > noise['volume']:
49 | continue
50 |
51 | if judgement.is_target_threat(life, target['life']['id']):
52 | if not _most_likely_target['target'] or target['last_seen_time'] < _most_likely_target['last_seen_time']:
53 | _most_likely_target['last_seen_time'] = target['last_seen_time']
54 | _most_likely_target['target'] = target
55 |
56 | if _most_likely_target['target']:
57 | _most_likely_target['target']['escaped'] = 1
58 | _most_likely_target['target']['last_seen_at'] = noise['pos'][:]
59 | _most_likely_target['target']['last_seen_time'] = 1
60 |
61 | logging.debug('%s heard a noise, attributing it to %s.' % (' '.join(life['name']), ' '.join(_most_likely_target['target']['life']['name'])))
62 |
63 | def _spread(noise):
64 | for alife in LIFE.values():
65 | if alife['dead']:
66 | continue
67 |
68 | _can_see = False
69 | if sight.can_see_position(alife, noise['pos']):
70 | _can_see = True
71 |
72 | _dist = bad_numbers.distance(noise['pos'], alife['pos'])
73 |
74 | if _dist>noise['volume']:
75 | continue
76 |
77 | update_targets_around_noise(alife, noise)
78 |
79 | _direction_to = bad_numbers.direction_to(alife['pos'], noise['pos'])
80 | _direction_string = language.get_real_direction(_direction_to)
81 |
82 | #TODO: Check walls between positions
83 | #TODO: Add memory
84 | if not _can_see or not noise['skip_on_visual']:
85 | if _dist >=noise['volume']/2:
86 | if 'player' in alife:
87 | gfx.message(random.choice(FAR_TEXT).replace('@t', noise['text'][1]).replace('@d', _direction_string), style='sound')
88 | else:
89 | if 'player' in alife:
90 | gfx.message(random.choice(FAR_TEXT).replace('@t', noise['text'][0]).replace('@d', _direction_string), style='sound')
91 |
--------------------------------------------------------------------------------
/network.py:
--------------------------------------------------------------------------------
1 | from globals import *
2 |
3 | import life as lfe
4 |
5 | import alife
6 |
7 | import threading
8 | import logging
9 | import socket
10 | import json
11 |
12 | def parse_packet(packet):
13 | try:
14 | _packet = json.loads(packet.strip())
15 | except:
16 | logging.error('Debug: Invalid packet.')
17 | return json.dumps({'type': 'text', 'text': 'Invalid packet from this client.'})
18 |
19 | if _packet['type'] == 'get':
20 | if _packet['what'] == 'stats':
21 | _stats = {'total_memories': sum([len(l['memory']) for l in LIFE.values()]),
22 | 'active_life': len([l for l in LIFE.values() if not l['dead']])}
23 |
24 | return json.dumps(_stats)
25 | elif _packet['what'] == 'groups':
26 | return json.dumps(WORLD_INFO['groups'])
27 | elif _packet['what'] == 'life':
28 | _life = LIFE[_packet['value']]
29 |
30 | _knows = {}
31 | for entry in _life['know'].values():
32 | _knows[entry['life']['id']] = {}
33 | for key in entry:
34 | if key == 'heard':
35 | continue
36 |
37 | if key == 'life':
38 | _knows[entry['life']['id']][key] = entry['life']['id']
39 | continue
40 |
41 | _knows[entry['life']['id']][key] = _life['know'][entry['life']['id']][key]
42 |
43 | if _life['job']:
44 | _job = _life['job']['gist']
45 | else:
46 | _job = None
47 |
48 | _sent_life = {'name': _life['name'],
49 | 'id': _life['id'],
50 | 'state': _life['state'],
51 | 'flags': _life['flags'],
52 | 'job': _job,
53 | 'group': _life['group'],
54 | 'tempstor': _life['tempstor2'],
55 | 'know': _knows,
56 | 'stats': _life['stats']}
57 |
58 | return json.dumps(_sent_life)
59 | elif _packet['what'] == 'memory':
60 | _memory = []
61 | for entry in LIFE[_packet['value']]['memory']:
62 | if 'question' in entry and entry['question']:
63 | _q = entry.copy()
64 | _memory.append(_q)
65 | continue
66 |
67 | _memory.append(entry)
68 |
69 | return json.dumps(_memory)
70 | elif _packet['what'] == 'life_list':
71 | return json.dumps(LIFE.keys())
72 | elif _packet['what'] == 'camp_list':
73 | return json.dumps(CAMPS.keys())
74 | elif _packet['what'] == 'camp':
75 | if not _packet['value'] in CAMPS:
76 | return json.dumps({})
77 |
78 | _camp = CAMPS[_packet['value']]
79 | _send_camp = _camp.copy()
80 | _send_camp['groups'] = alife.camps.get_controlling_groups(_camp['id'])
81 |
82 | return json.dumps(_send_camp)
83 |
84 |
85 | class DebugHost(threading.Thread):
86 | def __init__(self):
87 | self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
88 | self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
89 | self.socket.bind((SETTINGS['debug host'], SETTINGS['debug port']))
90 | self.socket.listen(1)
91 | self.socket.settimeout(3)
92 |
93 | threading.Thread.__init__(self)
94 |
95 | def quit(self):
96 | try:
97 | self.conn.close()
98 | except:
99 | pass
100 |
101 | self.socket.close()
102 | self.socket.shutdown(socket.SHUT_RDWR)
103 | logging.debug('Debug: Quit.')
104 |
105 | def run(self):
106 | logging.debug('DebugHost up.')
107 |
108 | while SETTINGS['running']:
109 | try:
110 | self.conn, self.addr = self.socket.accept()
111 | logging.debug('Debug: Connected.')
112 | except socket.timeout:
113 | continue
114 |
115 | data = self.conn.recv(1024)
116 | self.conn.sendall(parse_packet(data))
117 | self.conn.close()
--------------------------------------------------------------------------------
/fast_scan_surroundings.pyx:
--------------------------------------------------------------------------------
1 | from globals import WORLD_INFO
2 | from libc.stdlib cimport malloc, free
3 |
4 | import fast_dijkstra
5 | import life as lfe
6 |
7 | import drawing
8 | import numbers
9 | import alife
10 | import maps
11 |
12 | def scan_surroundings(life, initial=False, _chunks=[], ignore_chunks=[], judge=True, get_chunks=False, visible_check=True):
13 | cdef char *chunk_key = malloc((9+1))
14 | cdef int i, _chunk_x, _chunk_y, _dist
15 | cdef CHUNK_SIZE = WORLD_INFO['chunk_size']
16 |
17 | _center_chunk_key = lfe.get_current_chunk_id(life)
18 |
19 | if get_chunks:
20 | _center_chunk = maps.get_chunk(_center_chunk_key)
21 |
22 | _visible_chunks = set()
23 |
24 | if _chunks:
25 | _chunks = [c for c in _chunks if c in WORLD_INFO['chunk_map']]
26 | else:
27 | _temp_chunks = alife.sight._scan_surroundings(_center_chunk_key, CHUNK_SIZE, alife.sight.get_vision(life), ignore_chunks=ignore_chunks)
28 | _chunks = []
29 | for _chunk in _temp_chunks:
30 | if _chunk in WORLD_INFO['chunk_map']:
31 | _chunks.append(_chunk)
32 |
33 | #Find chunks furthest away
34 | if visible_check:
35 | _outline_chunks = {'distance': 0, 'chunks': []}
36 |
37 | for i in range(len(_chunks)):
38 | chunk_key = _chunks[i]
39 |
40 | _current_chunk = maps.get_chunk(chunk_key)
41 | _dist = numbers.distance(life['pos'], (_current_chunk['pos'][0]+CHUNK_SIZE/2, _current_chunk['pos'][1]+CHUNK_SIZE/2))
42 |
43 | if _dist>_outline_chunks['distance']+CHUNK_SIZE:
44 | _outline_chunks['distance'] = _dist
45 | _outline_chunks['chunks'] = [chunk_key]
46 | elif _dist>=_outline_chunks['distance']:
47 | _outline_chunks['chunks'].append(chunk_key)
48 |
49 | for outline_chunk_key in _outline_chunks['chunks']:
50 | if outline_chunk_key in _visible_chunks:
51 | continue
52 |
53 | _outline_chunk = WORLD_INFO['chunk_map'][outline_chunk_key]
54 | if _outline_chunk['max_z'] <= life['pos'][2]:
55 | _can_see = drawing.diag_line(life['pos'], (_outline_chunk['pos'][0]+CHUNK_SIZE/2, _outline_chunk['pos'][1]+CHUNK_SIZE/2))
56 | else:
57 | _can_see = alife.chunks.can_see_chunk(life, outline_chunk_key)
58 |
59 | if _can_see:
60 | _skip = 0
61 | for pos in _can_see:
62 | #if _skip:
63 | # _skip-=1
64 | # continue
65 |
66 | #if _can_see.index(pos)_l_slope:
73 | break
74 |
75 | _sax = _d_x * xx + _d_y * xy
76 | _say = _d_x * yx + _d_y * yy
77 |
78 | if (_sax<0 and abs(_sax)>x) or (_say<0 and abs(_say)>y):
79 | _d_x += 1
80 | continue
81 |
82 |
83 | _a_x = x + _sax
84 | _a_y = y + _say
85 |
86 | if _a_x >= MAP_SIZE[0] or _a_y >= MAP_SIZE[1]:
87 | _d_x += 1
88 | continue
89 |
90 | _rad2 = size*size
91 |
92 | if (_d_x * _d_x + _d_y * _d_y) < _rad2:
93 | los_map[_a_x, _a_y] = 1
94 |
95 | _solid = WORLD_INFO['map'][_a_x][_a_y]
96 | if _blocked:
97 | if _solid:
98 | _next_start_slope = _r_slope
99 | _d_x += 1
100 | continue
101 | else:
102 | _blocked = False
103 | start_slope = _next_start_slope
104 | elif _solid:
105 | _blocked = True
106 | _next_start_slope = _r_slope
107 | light(los_map, world_pos, size, i+1, start_slope, _l_slope, xx, xy, yx, yy)
108 |
109 | _d_x += 1
110 |
111 | if _blocked:
112 | break
113 |
114 |
115 | def los(start_position, distance):
116 | _los = numpy.zeros((distance*2, distance*2))
117 |
118 | for i in range(8):
119 | light(_los, start_position, distance, 1, 1.0, 0.0, MULT[0][i],
120 | MULT[1][i], MULT[2][i], MULT[3][i]);
121 | #walk_row(_los, (20, 20), octant=(-1, -1))
122 | #walk_row(_los, (20, 20), octant=(1, -1))
123 | #walk_row(_los, (20, 20), octant=(-1, 1))
124 | #walk_row(_los, (20, 20), octant=(1, 1))
125 |
126 | #walk_col(_los, (20, 20), octant=(-1, -1))
127 | #walk_col(_los, (20, 20), octant=(-1, 1))
128 | #walk_col(_los, (20, 20), octant=(1, -1))
129 | #walk_col(_los, (20, 20), octant=(1, 1))
130 |
131 | return _los
132 |
133 | _start_time = time.time()
134 | _los = los((20, 20), 20)
135 | print time.time()-_start_time
136 |
137 | draw(_los)
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | Reactor 3
2 | =========
3 |
4 | Note
5 | ----
6 |
7 | Please grab the `unstable` branch of the game when cloning. Some changes to
8 | Numpy have rendered the game unplayable due to a file conflict and I've renamed
9 | that file on that branch only. Also, **delete all instances of `numbers.pyc`**.
10 |
11 | I stopped working on this in 2014, but a successor is being privately developed
12 | using a new framework written in C. I will post on Reddit's `roguelike` or
13 | `roguelikedev` subforums eventually.
14 |
15 | Please do not use this game's code as a guide for working with Python / libtcod.
16 | I had never developed a project this size before and was learning along the way.
17 | R3 accomplished what it did via brute force and creative engineering. I don't
18 | recommend that way of working.
19 |
20 | What is Reactor 3?
21 | ------------------
22 | Reactor 3 is best described as a mix S.T.A.L.K.E.R. and Fallout with the additon of procedurally generated elements.
23 | There is a heavy emphasis on NPC interaction, squad tactics, and survival, with each action you make shaping the Zone,
24 | an unstable area surrounding the Chernobyl Nuclear Power Plant. The Zone itself is a living entity, causing erratic
25 | weather events and transforming the local wildlife into hostile mutants.
26 |
27 | R3 is brutally difficult and unforgiving, punishing those who choose a run-'n-gun playstyle over non-combat solutions.
28 | Combat is heavily grounded in reality, modeling minor injuries like scrapes and cuts to full dismemberment. The player's
29 | inventory is also treated as it should; each item must be either held, worn, or stored away in a container (backpack,
30 | pocket, etc,) which encourages the player to not only pick and choose between what they carry, but also how they carry
31 | it (a pistol would be stored in a holster for quicker access, for example.)
32 |
33 | Join a faction and take over the Zone, or simply exist on your own.
34 |
35 | Installing
36 | ==========
37 | Reactor 3 requires Python 2.7, Cython, Numpy, and [libtcod](http://doryen.eptalys.net/libtcod/download/).
38 |
39 | git clone https://github.com/flags/Reactor-3.git
40 | cd Reactor-3
41 | python compile_cython_modules.py build_ext --inplace
42 |
43 | Next, download the libtcod library and move the `.so` (Windows: `.dll`) files from the archive to the Reactor 3 directory.
44 |
45 | Run `python reactor-3.py` to play.
46 |
47 | See the section `flags` below for more info.
48 |
49 | Controls
50 | ========
51 | * `Arrow keys` - Move (4-way movement)/Navigate menus
52 | * `Numpad` - Moe (8-way movement)
53 | * `Enter` - Select
54 | * `e` - Equip/hold item
55 | * `E (Shift-e)` - Unequip item
56 | * `,` - Pick up item
57 | * `d` - Drop item
58 | * `r` - Reload / fill mag or clip
59 | * `f` - Enter targeting mode (shoot)
60 | * `F (Shift-f)` - Set fire mode
61 | * `v` - Enter targeting mode (talk)
62 | * `V (Shift-v)` - Use radio
63 | * `Tab` - Group communication
64 | * `k` - Open crafting menu
65 | * `w` - Open medical menu
66 | * `W (Shift-w)` - Open medical menu (heal someone)
67 | * `C (Shift-c)` - Stand up
68 | * `c` - Crouch
69 | * `Z (Shift-z)` - Prone
70 | * `P (Shift-P)` - Pause
71 | * `l` - Look
72 | * `O (Shift-o)` - Options
73 | * `-` - Debug console
74 | * `?` - Record mode (**This will dump potentially hundreds of .BMP files in the game's directory**)
75 |
76 | Flags
77 | -----
78 | `reactor-3.py` can be run with a few arguments:
79 |
80 | * `--quick` - Load latest game.
81 | * `--profile` - Dumps a profile to `profile.dat`. `tools/show_profile.py` can be used to view the profile (use the argument `highest` to show the most time consuming functions)
82 |
83 | Credits
84 | -------
85 | Reactor 3 is made possible by the `libtcod` library. All other work was done by flags, a member of the ASCII Worlds Collective.
86 |
--------------------------------------------------------------------------------
/cache.py:
--------------------------------------------------------------------------------
1 | from globals import *
2 |
3 | import profiles
4 | import items
5 |
6 | import logging
7 | import json
8 |
9 | import os
10 |
11 | def _capsule():
12 | return {'uid': None,
13 | 'date': WORLD_INFO['ticks'],
14 | '_on_disk': False,
15 | '_allow_dump': False}
16 |
17 | def _write_to_cache(cache_name, data):
18 | with open(os.path.join(profiles.get_world_directory(WORLD_INFO['id']), '%s_history.dat' % cache_name), 'a') as f:
19 | f.write(json.dumps(data)+'\n\n')
20 |
21 | def _read_from_cache(cache_name, uid):
22 | with open(os.path.join(profiles.get_world_directory(WORLD_INFO['id']), '%s_history.dat' % cache_name)) as f:
23 | for item in [json.loads(s) for s in f]:
24 | if item['uid'] == uid:
25 | item['_on_disk'] = False
26 | item['date'] = WORLD_INFO['ticks']
27 | ITEMS_HISTORY[item['uid']].update(item)
28 |
29 | logging.debug('Cache: Loaded item %s from cache.' % uid)
30 | return item
31 |
32 | def save_cache(cache_name):
33 | _path = os.path.join(profiles.get_world_directory(WORLD_INFO['id']), '%s_history.dat' % cache_name)
34 | _write_cache = []
35 |
36 | if not os.path.exists(_path):
37 | return False
38 |
39 | with open(_path, 'r') as f:
40 | _cache = f.readlines()
41 |
42 | for _line in _cache:
43 | line = _line.rstrip()
44 |
45 | if line == '\n' or not line:
46 | continue
47 |
48 | _historic_item = json.loads(line)
49 |
50 | if not _historic_item['_allow_dump']:
51 | continue
52 |
53 | _dump_string = json.dumps(_historic_item)
54 |
55 | _write_cache.append(_dump_string)
56 |
57 | with open(_path, 'w') as f:
58 | f.write('\n'.join(_write_cache))
59 |
60 | logging.debug('Cache: Saved to disk.')
61 |
62 | def commit_cache(cache_name):
63 | _path = os.path.join(profiles.get_world_directory(WORLD_INFO['id']), '%s_history.dat' % cache_name)
64 | _write_cache = []
65 |
66 | if not os.path.exists(_path):
67 | return False
68 |
69 | with open(_path, 'r') as f:
70 | _cache = f.readlines()
71 |
72 | for _line in _cache:
73 | line = _line.rstrip()
74 |
75 | if line == '\n' or not line:
76 | continue
77 |
78 | print repr(line)
79 | _historic_item = json.loads(line)
80 | _historic_item['_allow_dump'] = True
81 | _write_cache.append(json.dumps(_historic_item))
82 |
83 | if cache_name == 'items':
84 | for item_uid in ITEMS_HISTORY:
85 | _historic_item = ITEMS_HISTORY[item_uid]
86 | if not _historic_item['_on_disk']:
87 | continue
88 |
89 | _historic_item['_allow_dump'] = True
90 |
91 | with open(_path, 'w') as f:
92 | f.write('\n'.join(_write_cache))
93 |
94 | logging.debug('Cache: Committed.')
95 |
96 | def scan_cache():
97 | return False
98 |
99 | for item_uid in ITEMS_HISTORY:
100 | _historic_item = ITEMS_HISTORY[item_uid]
101 |
102 | if _historic_item['_on_disk']:
103 | continue
104 |
105 | if WORLD_INFO['ticks']-_historic_item['date']>=100:
106 | logging.debug('Cache: Moved item %s to disk' % item_uid)
107 | _historic_item['_on_disk'] = True
108 | _write_to_cache('items', _historic_item)
109 |
110 | if 'item' in _historic_item:
111 | del _historic_item['item']
112 |
113 | def offload_item(raw_item):
114 | _item = _capsule()
115 | items.clean_item_for_save(raw_item)
116 | _item['uid'] = raw_item['uid']
117 | _item['item'] = raw_item
118 |
119 | ITEMS_HISTORY[_item['uid']] = _item
120 |
121 | logging.debug('Cache: Offloaded item (in memory)')
122 |
123 | def retrieve_item(item_uid):
124 | _historic_item = ITEMS_HISTORY[item_uid]
125 |
126 | if _historic_item['_on_disk']:
127 | raise Exception('Ahhhhhhhhhhhh')
128 | #TODO: Grab from disk
129 | pass
130 |
131 | return _historic_item['item']
--------------------------------------------------------------------------------
/overwatch/events.py:
--------------------------------------------------------------------------------
1 | from globals import WORLD_INFO, SETTINGS, LIFE, ITEMS
2 |
3 | import graphics as gfx
4 | import libtcodpy as tcod
5 |
6 | import artifacts
7 | import language
8 | import bad_numbers
9 | import drawing
10 | import effects
11 | import spawns
12 | import items
13 | import alife
14 | import maps
15 | import core
16 |
17 | import logging
18 | import random
19 |
20 |
21 | def create_heli_crash(pos, spawn_list):
22 | _size = random.randint(4, 6)
23 |
24 | effects.create_explosion(pos, _size)
25 |
26 | for n_pos in drawing.draw_circle(pos, _size*2):
27 | if random.randint(0, 10):
28 | continue
29 |
30 | _n_pos = list(n_pos)
31 | _n_pos.append(2)
32 |
33 | effects.create_fire(_n_pos, intensity=8)
34 |
35 | def create_cache_drop(pos, spawn_list):
36 | _player = LIFE[SETTINGS['controlling']]
37 | _pos = spawns.get_spawn_point_around(pos, area=10)
38 | _direction = language.get_real_direction(bad_numbers.direction_to(_player['pos'], _pos))
39 |
40 | for container in spawn_list:
41 | if not container['rarity']>random.uniform(0, 1.0):
42 | continue
43 |
44 | _c = items.create_item(container['item'], position=[_pos[0], _pos[1], 2])
45 |
46 | for _inside_item in container['spawn_list']:
47 | if _inside_item['rarity']<=random.uniform(0, 1.0):
48 | continue
49 |
50 | _i = items.create_item(_inside_item['item'], position=[_pos[0], _pos[1], 2])
51 |
52 | if not items.can_store_item_in(_i, _c):
53 | items.delete_item(_i)
54 |
55 | continue
56 |
57 | items.store_item_in(_i, _c)
58 |
59 | effects.create_smoker(_pos, 300, color=tcod.orange)
60 |
61 | gfx.message('You see something parachuting to the ground to the %s.' % _direction, style='event')
62 |
63 | def create_anomaly_field(situation, y_min=0):
64 | return artifacts.create_field(y_min=y_min)
65 |
66 | def spawn_life(life_type, position, event_time, **kwargs):
67 | _life = {'type': life_type, 'position': position[:]}
68 | _life.update(**kwargs)
69 |
70 | WORLD_INFO['scheme'].append({'life': _life, 'time': WORLD_INFO['ticks']+event_time})
71 |
72 | def order_group(life, group_id, stage, event_time, **kwargs):
73 | WORLD_INFO['scheme'].append({'group': group_id,
74 | 'member': life['id'],
75 | 'stage': stage,
76 | 'flags': kwargs,
77 | 'time': WORLD_INFO['ticks']+event_time})
78 |
79 | def broadcast(messages, event_time, glitch=False):
80 | _time = WORLD_INFO['ticks']+event_time
81 | _i = 0
82 |
83 | for entry in messages:
84 | if 'source' in entry:
85 | _source = entry['source']
86 | else:
87 | _source = '???'
88 |
89 |
90 | if glitch:
91 | if 'change_only' in entry:
92 | _change = entry['change_only']
93 | else:
94 | _change = False
95 |
96 | _delay = (50*bad_numbers.clip(_i, 0, 1))+(len(entry['text'])*2)*_i
97 |
98 | WORLD_INFO['scheme'].append({'glitch': entry['text'], 'change': _change, 'time': _time+_delay})
99 | else:
100 | WORLD_INFO['scheme'].append({'radio': [_source, entry['text']], 'time': _time})
101 |
102 | _time += int(round(len(entry['text'])*1.25))
103 | _i += 1
104 |
105 | def sound(near_text, far_text, position, volume, time):
106 | WORLD_INFO['scheme'].append({'sound': (near_text, far_text), 'pos': position, 'time': WORLD_INFO['ticks']+time, 'volume': volume})
107 |
108 | def attract_tracked_alife_to(pos):
109 | _chunk_key = alife.chunks.get_chunk_key_at(pos)
110 |
111 | for ai in [LIFE[i] for i in WORLD_INFO['overwatch']['tracked_alife']]:
112 | alife.movement.set_focus_point(ai, _chunk_key)
113 |
114 | logging.debug('[Overwatch]: Attracting %s to %s.' % (' '.join(ai['name']), _chunk_key))
115 |
116 |
117 | FUNCTION_MAP = {'heli_crash': create_heli_crash,
118 | 'cache_drop': create_cache_drop}
--------------------------------------------------------------------------------
/alife/memory.py:
--------------------------------------------------------------------------------
1 | from globals import *
2 |
3 | import life as lfe
4 |
5 | import judgement
6 | import rawparse
7 | import action
8 | import speech
9 | import brain
10 | import jobs
11 |
12 | import logging
13 |
14 | def create_question(life, life_id, gist, ignore_if_said_in_last=0, recent_time=0, **kwargs):
15 | _target = brain.knows_alife_by_id(life, life_id)
16 | _question = {'gist': gist, 'args': kwargs}
17 |
18 | if not _target:
19 | logging.critical('%s does not know %s but is creating questions for them.' % (' '.join(life['name']), ' '.join(LIFE[life_id]['name'])))
20 | return False
21 |
22 | if _target['time_visible'] < recent_time:
23 | return False
24 |
25 | if _question in _target['questions']:
26 | return False
27 |
28 | _sent = speech.has_sent(life, life_id, gist)
29 |
30 | if _sent and (WORLD_INFO['ticks']-_sent', p['next_stance']['stance']
88 |
89 | p['stance'] = p['next_stance']['stance']
90 | p['next_stance']['stance'] = None
91 |
92 | return True
93 |
94 | def perform_moves(people):
95 | for life in people:
96 | if not life['stance'] in COMBAT_MOVES:
97 | continue
98 |
99 | if life['next_stance']['towards']:
100 | _target = life['next_stance']['towards']
101 |
102 | if life['stance'] in COMBAT_MOVES and _target['stance'] in COMBAT_MOVES[life['stance']]['counters']:
103 | print '%s counters %s\'s %s!' % (_target['name'], life['name'], life['stance'])
104 |
105 | force_stance(life, 'off-balance')
106 | else:
107 | if _target['stance'] in 'off-balance':
108 | force_stance(_target, 'prone')
109 |
110 | print '%s\'s %s hits %s!' % (life['name'], life['stance'], _target['name'])
111 |
112 | life['next_stance']['towards'] = None
113 | else:
114 | print '%s\'s %s does nothing!' % (life['name'], life['stance'])
115 |
116 | #TODO: React...
117 | #life['stance'] = 'stand'
118 |
119 | assume_stance(p1, 'punch', towards=p2)
120 |
121 | t = 7
122 | while t:
123 | examine_possible_moves(p1, LIFE)
124 | examine_possible_moves(p2, LIFE)
125 |
126 | tick(p1)
127 | tick(p2)
128 |
129 | perform_moves([p1, p2])
130 | t -= 1
--------------------------------------------------------------------------------
/prefabs.py:
--------------------------------------------------------------------------------
1 | #Tools for generating buildings (prefabs)
2 | from globals import DATA_DIR
3 | from tiles import *
4 |
5 | import graphics as gfx
6 |
7 | import menus
8 |
9 | import logging
10 | import random
11 | import json
12 | import os
13 |
14 | def create_new_prefab(size):
15 | if not len(size) == 3:
16 | raise Exception('Invalid prefab size: Expected 3 arguments.')
17 |
18 | _prefab = []
19 |
20 | for x in range(size[0]):
21 | _y = []
22 | for y in range(size[1]):
23 | _z = []
24 | for z in range(size[2]):
25 | #if z==0:
26 | # _z.append(create_tile(TALL_GRASS_TILE))
27 | #else:
28 | _z.append(None)
29 |
30 | _y.append(_z)
31 | _prefab.append(_y)
32 |
33 | logging.debug('Created new prefab of size (%s,%s,%s).' % (size[0],size[1],size[2]))
34 |
35 | return {'map': _prefab,'size': size}
36 |
37 | def cache_prefab(name, path):
38 | with open(path, 'r') as f:
39 | _prefab = json.loads(''.join(f.readlines()))
40 | _prefab['name'] = name
41 |
42 | PREFABS[name] = _prefab
43 | logging.debug('Prefab cached: %s' % name)
44 |
45 | def cache_all_prefabs():
46 | logging.debug('Caching all prefabs...')
47 | for (dirpath, dirname, filenames) in os.walk(PREFAB_DIR):
48 | for f in [f for f in filenames if f.count('.json')]:
49 | cache_prefab(f.partition('.')[0], os.path.join(PREFAB_DIR, f))
50 |
51 | def save(prefab):
52 | with open(os.path.join(PREFAB_DIR, 'test.json'), 'w') as f:
53 | f.write(json.dumps(prefab))
54 |
55 | def _draw_prefab(prefab):
56 | _X_MAX = PREFAB_CAMERA_POS[0]+PREFAB_WINDOW_SIZE[0]
57 | _Y_MAX = PREFAB_CAMERA_POS[1]+PREFAB_WINDOW_SIZE[1]
58 |
59 | #DARK_BUFFER[0] = numpy.zeros((MAP_WINDOW_SIZE[1], PREFAB_WINDOW_SIZE[0]))
60 | #LIGHT_BUFFER[0] = numpy.zeros((MAP_WINDOW_SIZE[1], MAP_WINDOW_SIZE[0]))
61 |
62 | map = prefab['map']
63 |
64 | if _X_MAX>prefab['size'][0]:
65 | _X_MAX = prefab['size'][0]
66 |
67 | if _Y_MAX>prefab['size'][1]:
68 | _Y_MAX = prefab['size'][1]
69 |
70 | for x in range(PREFAB_CAMERA_POS[0],_X_MAX):
71 | _RENDER_X = x-PREFAB_CAMERA_POS[0]
72 |
73 | for y in range(PREFAB_CAMERA_POS[1],_Y_MAX):
74 | _RENDER_Y = y-PREFAB_CAMERA_POS[1]
75 | _drawn = False
76 |
77 | for z in range(prefab['size'][2]-1,-1,-1):
78 | if map[x][y][z]:
79 | gfx.blit_tile(_RENDER_X,
80 | _RENDER_Y,
81 | map[x][y][z],
82 | char_buffer=PREFAB_CHAR_BUFFER,
83 | rgb_fore_buffer=PREFAB_RGB_FORE_BUFFER,
84 | rgb_back_buffer=PREFAB_RGB_BACK_BUFFER)
85 |
86 | _drawn = True
87 | break
88 | #if z > PREFAB_CAMERA_POS[2] and SETTINGS['draw z-levels above']:
89 | # gfx.blit_tile(_RENDER_X,_RENDER_Y,map[x][y][z])
90 | # gfx.darken_tile(_RENDER_X,_RENDER_Y,abs((PREFAB_CAMERA_POS[2]-z))*30)
91 | # _drawn = True
92 | #elif z == PREFAB_CAMERA_POS[2]:
93 | # if (x,y,z) in SELECTED_TILES[0] and time.time()%1>=0.5:
94 | # gfx.blit_char(_RENDER_X,_RENDER_Y,'X',darker_grey,black)
95 | # else:
96 | # gfx.blit_tile(_RENDER_X,_RENDER_Y,map[x][y][z])
97 | # _drawn = True
98 | #elif z < PREFAB_CAMERA_POS[2] and SETTINGS['draw z-levels below']:
99 | # gfx.blit_tile(_RENDER_X,_RENDER_Y,map[x][y][z])
100 | # gfx.darken_tile(_RENDER_X,_RENDER_Y,abs((PREFAB_CAMERA_POS[2]-z))*30)
101 | # _drawn = True
102 |
103 | #if SETTINGS['draw z-levels above'] and _drawn:
104 | # break
105 |
106 | if not _drawn:
107 | gfx.blit_tile(_RENDER_X,
108 | _RENDER_Y,
109 | BLANK_TILE,
110 | char_buffer=PREFAB_CHAR_BUFFER,
111 | rgb_fore_buffer=PREFAB_RGB_FORE_BUFFER,
112 | rgb_back_buffer=PREFAB_RGB_BACK_BUFFER)
113 |
114 | def draw_prefab_thumbnail(entry):
115 | #VIEWS
116 | key = entry['key']
117 | value = entry['values'][entry['value']]
118 | _prefab = PREFABS[key]
119 |
120 | PREFAB_TOP_VIEW_POS = (15, 1)
121 |
122 | for y in range(0, _prefab['size'][1]):
123 | for x in range(0, _prefab['size'][0]):
124 | for z in range(0, _prefab['size'][2]):
125 | if not _prefab['map'][x][y][z]:
126 | continue
127 |
128 | print _prefab['map'][x][y][z]
129 | #tcod.console_put_char_(0, PREFAB_TOP_VIEW_POS[0]+x, PREFAB_TOP_VIEW_POS[1]+y,
130 |
131 | def prefab_selected(entry):
132 | pass
133 |
134 | def create_prefab_list():
135 | tcod.console_clear(0)
136 | tcod.console_clear(MAP_WINDOW)
137 | _prefabs = []
138 | for prefab in PREFABS.values():
139 | _prefabs.append(menus.create_item('single', prefab['name'], None))
140 |
141 | return menus.create_menu(title='Prefabs',
142 | menu=_prefabs,
143 | padding=(0, 0),
144 | position=(0, 0),
145 | format_str='$k',
146 | on_select=prefab_selected,
147 | on_move=draw_prefab_thumbnail)
--------------------------------------------------------------------------------
/tests/M01_map_editor.py:
--------------------------------------------------------------------------------
1 | from libtcodpy import *
2 |
3 | console_init_root(100, 50, 'M01 - Visualization',renderer=RENDERER_OPENGL)
4 | console_set_custom_font('terminal12x12_gs_ro.png',FONT_LAYOUT_ASCII_INCOL)
5 | sys_set_fps(30)
6 |
7 | SHORT_GRASS_TILE = {'id':'short_grass',
8 | 'icon':'.',
9 | 'color':(light_green,Color(0,230,0)),
10 | 'burnable':True,
11 | 'cost':1}
12 |
13 | GRASS_TILE = {'id':'grass',
14 | 'icon':',',
15 | 'color':(green,Color(0,210,0)),
16 | 'burnable':True,
17 | 'cost':1}
18 |
19 | TALL_GRASS_TILE = {'id':'tall_grass',
20 | 'icon':';',
21 | 'color':(dark_green,Color(0,205,0)),
22 | 'burnable':True,
23 | 'cost':1}
24 |
25 | DIRT_TILE = {'id':'dirt',
26 | 'icon':'.',
27 | 'color':(gray,gray),
28 | 'burnable':False,
29 | 'cost':2}
30 |
31 | WALL_TILE = {'id':'wall',
32 | 'icon':'#',
33 | 'color':(black,gray),
34 | 'burnable':False,
35 | 'cost':-1}
36 |
37 | TILES = [SHORT_GRASS_TILE,GRASS_TILE,TALL_GRASS_TILE,DIRT_TILE,WALL_TILE]
38 | MAP = []
39 | MAP_SIZE = (100,50,5)
40 | CAMERA_Z_LEVEL = 1
41 | KEY = Key()
42 | MOUSE_POS = (0,0)
43 | MOUSE_1_DOWN = False
44 | MOUSE = Mouse()
45 | CURSOR_POS = [0,0]
46 | mouse_move(0,0)
47 |
48 | def create_tile(tile):
49 | _ret_tile = {}
50 | _ret_tile['id'] = tile['id']
51 |
52 | _ret_tile['items'] = []
53 | _ret_tile['fire'] = 0
54 |
55 | return _ret_tile
56 |
57 | def get_raw_tile(tile):
58 | for _tile in TILES:
59 | if _tile['id'] == tile['id']:
60 | return _tile
61 |
62 | raise Exception
63 |
64 | import random
65 |
66 | for x in range(MAP_SIZE[0]):
67 | _y = []
68 | for y in range(MAP_SIZE[1]):
69 | _z = []
70 | for z in range(MAP_SIZE[2]):
71 | if z == 0:
72 | _z.append(create_tile(DIRT_TILE))
73 | elif z == 1:
74 | _z.append(create_tile(random.choice([GRASS_TILE,SHORT_GRASS_TILE,TALL_GRASS_TILE])))
75 | else:
76 | _z.append(None)
77 |
78 | _y.append(_z)
79 | MAP.append(_y)
80 |
81 | MAP[3][3][2]=create_tile(GRASS_TILE)
82 |
83 | def get_mouse_input():
84 | global MOUSE,MOUSE_POS,MOUSE_1_DOWN
85 |
86 | if MOUSE.lbutton:
87 | if not MOUSE_1_DOWN:
88 | MOUSE_1_DOWN = True
89 | else:
90 | MOUSE_1_DOWN = False
91 |
92 | MOUSE_POS = MOUSE.x/16,MOUSE.y/16
93 |
94 | def get_input():
95 | global KEY,MOUSE,CURSOR_POS
96 |
97 | sys_check_for_event(EVENT_KEY_PRESS|EVENT_MOUSE,KEY,MOUSE)
98 |
99 | if KEY.vk == KEY_UP:
100 | if CURSOR_POS[1]>0: CURSOR_POS[1]-=1
101 | elif KEY.vk == KEY_DOWN:
102 | if CURSOR_POS[1]0: CURSOR_POS[0]-=1
106 | elif KEY.vk == KEY_RIGHT:
107 | if CURSOR_POS[0]2:
16 | size = 2
17 |
18 | cdef float circle = 0
19 | cdef int width=size
20 | cdef int height=size
21 | cdef float center_x=(width/2.0)
22 | cdef float center_y=(height/2.0)
23 | cdef int i,j
24 |
25 | _circle = []
26 |
27 | for i in range(height+1):
28 | for j in range(width+1):
29 | circle = (((i-center_y)*(i-center_y))/((float(height)/2)*(float(height)/2)))+((((j-center_x)*(j-center_x))/((float(width)/2)*(float(width)/2))));
30 | if circle>0 and circle<1.1:
31 | _circle.append((x+(j-(width/2)),y+(i-(height/2))))
32 |
33 | if not (x, y) in _circle:
34 | _circle.append((x, y))
35 |
36 | return _circle
37 |
38 | def swap(n1,n2):
39 | return [n2,n1]
40 |
41 | @cython.locals(x1=cython.int, y1=cython.int, x2=cython.int, y2=cython.int)
42 | def draw_line(x1,y1,x2,y2):
43 | path = []
44 | if (x1, y1) == (x2, y2):
45 | return [(x2, y2)]
46 |
47 | start = [x1,y1]
48 | end = [x2,y2]
49 |
50 | steep = abs(end[1]-start[1]) > abs(end[0]-start[0])
51 |
52 | if steep:
53 | start = swap(start[0],start[1])
54 | end = swap(end[0],end[1])
55 |
56 | if start[0] > end[0]:
57 | start[0],end[0] = swap(start[0],end[0])
58 | start[1],end[1] = swap(start[1],end[1])
59 |
60 | dx = end[0] - start[0]
61 | dy = abs(end[1] - start[1])
62 | error = 0
63 |
64 | try:
65 | derr = dy/float(dx)
66 | except:
67 | return None
68 |
69 | ystep = 0
70 | y = start[1]
71 |
72 | if start[1] < end[1]: ystep = 1
73 | else: ystep = -1
74 |
75 | for x in range(start[0],end[0]+1):
76 | if steep:
77 | path.append((y,x))
78 | else:
79 | path.append((x,y))
80 |
81 | error += derr
82 |
83 | if error >= 0.5:
84 | y += ystep
85 | error -= 1.0
86 |
87 | if not path[0] == (x1,y1):
88 | path.reverse()
89 |
90 | return path
91 |
92 | def render_los(position, size, view_size=MAP_WINDOW_SIZE, top_left=CAMERA_POS, no_edge=False, visible_chunks=None, life=None):
93 | los_buffer = numpy.zeros((view_size[1], view_size[0]))
94 |
95 | cdef int _dark = 0
96 | cdef int _x,_y
97 | cdef int X_CAMERA_POS = top_left[0]
98 | cdef int Y_CAMERA_POS = top_left[1]
99 | cdef int Z_CAMERA_POS = top_left[2]
100 | cdef int X_MAP_SIZE = MAP_SIZE[0]
101 | cdef int Y_MAP_SIZE = MAP_SIZE[1]
102 | cdef int X_MAP_WINDOW_SIZE = view_size[0]
103 | cdef int Y_MAP_WINDOW_SIZE = view_size[1]
104 | cdef int POS_X, POS_Y
105 | POS_X = position[0]
106 | POS_Y = position[1]
107 |
108 | #SKIP_CHUNKS = []
109 | VISIBLE_CHUNKS = []
110 | if life and alife.brain.get_flag(life, 'visible_chunks'):
111 | VISIBLE_CHUNKS = alife.brain.get_flag(life, 'visible_chunks')
112 |
113 | #for chunk_key in alife.brain.get_flag(life, 'visible_chunks'):
114 | # if maps.get_chunk(chunk_key)['max_z'] > life['pos'][2]:
115 | # SKIP_CHUNKS.append(chunk_key)
116 |
117 | #if not POS_X-X_CAMERA_POS<0 and not POS_X-X_CAMERA_POS >= X_MAP_WINDOW_SIZE and not POS_Y-Y_CAMERA_POS<0 and not POS_Y-Y_CAMERA_POS >= Y_MAP_WINDOW_SIZE:
118 | # los_buffer[POS_Y-Y_CAMERA_POS,POS_X-X_CAMERA_POS] = 1
119 |
120 | for _pos in draw_circle(POS_X, POS_Y, size):
121 | _dark = 0
122 |
123 | _chunk_key = '%s,%s' % ((_pos[0]/WORLD_INFO['chunk_size'])*WORLD_INFO['chunk_size'],
124 | (_pos[1]/WORLD_INFO['chunk_size'])*WORLD_INFO['chunk_size'])
125 |
126 | #if _chunk_key in HIDDEN_CHUNKS:
127 | # continue
128 |
129 | for pos in draw_line(POS_X,POS_Y,_pos[0],_pos[1]):
130 | _x = pos[0]-X_CAMERA_POS
131 | _y = pos[1]-Y_CAMERA_POS
132 | #_distance = 1#numbers.clip(numbers.distance(pos, (POS_X, POS_Y), old=True), 1, 255)*.10
133 |
134 | if _x<0 or _x>=X_MAP_WINDOW_SIZE or _y<0 or _y>=Y_MAP_WINDOW_SIZE:
135 | continue
136 |
137 | if pos[0]<0 or pos[0]>=X_MAP_SIZE or pos[0]<0 or pos[1]>=Y_MAP_SIZE:
138 | continue
139 |
140 | if maps.is_solid((pos[0], pos[1], Z_CAMERA_POS+1)):
141 | if not _dark:
142 | if not no_edge:
143 | los_buffer[_y,_x] = 1
144 |
145 | break
146 | else:
147 | _chunk = alife.chunks.get_chunk(alife.chunks.get_chunk_key_at((pos[0], pos[1], Z_CAMERA_POS)))
148 | _break = False
149 |
150 | for item_uid in _chunk['items'][:]:
151 | if not item_uid in ITEMS:
152 | _chunk['items'].remove(item_uid)
153 |
154 | for item in [ITEMS[uid] for uid in _chunk['items'] if items.is_blocking(uid)]:
155 | if tuple(item['pos']) == (pos[0], pos[1], Z_CAMERA_POS):
156 | if not _dark:
157 | if not no_edge:
158 | los_buffer[_y,_x] = 1
159 |
160 | _break = True
161 | break
162 |
163 | if _break:
164 | break
165 |
166 | los_buffer[_y,_x] = 1
167 |
168 | return los_buffer
169 |
--------------------------------------------------------------------------------
/data/life/human.goap:
--------------------------------------------------------------------------------
1 | #Flags:
2 | # - Check for desire, but don't try to meet it
3 | # * If it passes, great! If not, it's OK
4 | # ! Invert
5 | # @ World call (life entity not passed)
6 |
7 | #Goals
8 |
9 | #[GOAL_DISCOVER]
10 | #DESIRE: HAS_NON_RELAXED_GOAL
11 | #TIER: RELAXED
12 |
13 | [GOAL_FOLLOW]
14 | DESIRE: !HAS_TARGET_TO_FOLLOW
15 | TIER: SURVIVAL
16 |
17 | [GOAL_WATCH]
18 | DESIRE: !HAS_TARGET_TO_GUARD
19 | TIRE: URGENT
20 |
21 | #[GOAL_LOOT]
22 | #DESIRE: !HAS_NEEDS
23 | #TIER: SURVIVAL
24 | #SET_FLAGS: {"wanted_items": GET_NEEDED_ITEMS}
25 |
26 | [GOAL_GUARD]
27 | DESIRE: !HAS_FOCUS_POINT
28 | REQUIRE: !HAS_THREATS
29 | TIER: SURVIVAL
30 |
31 | #[GOAL_CAMP]
32 | #DESIRE: -*IS_IN_SHELTER,-!%IS_NIGHT,-HAS_SHELTER
33 | #REQUIRE: %IS_NIGHT
34 | #TIER: URGENT
35 |
36 | [GOAL_ATTACK]
37 | DESIRE: !HAS_THREATS
38 | REQUIRE: IS_CONFIDENT,HAS_USABLE_WEAPON,HAS_VISIBLE_THREAT,!HAS_HIGH_RECOIL
39 | TIER: TACTIC
40 |
41 | [GOAL_ATTACK_PRESSURE]
42 | DESIRE: !HAS_THREATS
43 | REQUIRE: !IS_CONFIDENT,HAS_USABLE_WEAPON,HAS_VISIBLE_THREAT,!HAS_HIGH_RECOIL,DANGER_CLOSE
44 | TIER: TACTIC
45 |
46 | [GOAL_TAKE_POINT]
47 | DESIRE: !HAS_FOCUS_POINT
48 | REQUIRE: !IS_CONFIDENT,IS_RAIDING
49 | TIER: URGENT
50 |
51 | #[GOAL_GET_SHELTER]
52 | #DESIRE: IS_IN_SHELTER,-!%IS_NIGHT
53 | #TIER: URGENT
54 |
55 | [GOAL_MANAGE_INVENTORY]
56 | DESIRE: !NEEDS_TO_MANAGE_INVENTORY
57 | TIER: COMBAT
58 |
59 | [GOAL_SEARCH_FOR_TARGET]
60 | DESIRE: !HAS_LOST_THREAT
61 | REQUIRE: !HAS_THREATS,HAS_LOST_THREAT,WEAPON_EQUIPPED_AND_READY,IS_CONFIDENT
62 | TIER: COMBAT
63 |
64 | [GOAL_COVER]
65 | DESIRE: !HAS_VISIBLE_THREAT
66 | REQUIRE: !IS_CONFIDENT
67 | TIER: URGENT
68 |
69 | [GOAL_HIDE_FROM_TARGET]
70 | DESIRE: !HAS_LOST_THREAT
71 | REQUIRE: !IS_CONFIDENT,!HAS_VISIBLE_THREAT
72 | TIER: URGENT
73 |
74 | #[GOAL_HIDE]
75 | #DESIRE: IS_IN_SHELTER
76 | #REQUIRE: !IS_CONFIDENT,HAS_VISIBLE_THREAT
77 | #TIER: URGENT
78 |
79 | [GOAL_COVER_FOR_REARM]
80 | DESIRE: !HAS_VISIBLE_THREAT
81 | REQUIRE: HAS_POTENTIALLY_USABLE_WEAPON,!WEAPON_EQUIPPED_AND_READY
82 | TIER: TACTIC
83 |
84 | #[GOAL_PREPARE]
85 | #DESIRE: !NEEDS_TO_MANAGE_INVENTORY
86 | #REQUIRE: !HAS_VISIBLE_THREAT,HAS_LOST_THREAT,HAS_POTENTIALLY_USABLE_WEAPON
87 | #TIER: TACTIC
88 |
89 | #[GOAL_RETREAT]
90 | #DESIRE: !HAS_VISIBLE_THREAT
91 | #REQUIRE: !IS_CONFIDENT,!WEAPON_EQUIPPED_AND_READY
92 | #TIER: TACTIC
93 |
94 | #[GOAL_EVADE]
95 | #DESIRE: -!HAS_LOST_THREAT,!HAS_NEEDS
96 | #TIER: TACTIC
97 |
98 | #[GOAL_TAKE_COVER]
99 | #DESIRE: !DANGER_CLOSE,-HAS_USABLE_WEAPON
100 | #TIER: TACTIC
101 |
102 | [GOAL_REPOSITION]
103 | DESIRE: !COVER_EXPOSED
104 | TIER: TACTIC
105 |
106 | [GOAL_MISSION]
107 | DESIRE: !HAS_MISSION
108 | TIER: URGENT
109 |
110 | #Actions
111 |
112 | #[ACTION_WANDER]
113 | #DESIRES: !NEEDS_TO_MANAGE_INVENTORY
114 | #SATISFIES: HAS_NON_RELAXED_GOAL
115 | #LOOP_UNTIL: NEVER
116 | #EXECUTE: WANDER
117 |
118 | [ACTION_GO_TO]
119 | DESIRES: !WEAPON_IS_ARMED
120 | SATISFIES: !HAS_FOCUS_POINT
121 | LOOP_UNTIL: NEVER
122 | EXECUTE: WALK_TO
123 |
124 | [ACTION_PICK_UP_ITEM]
125 | SATISFIES: !HAS_NEEDS
126 | DESIRES: KNOWS_ABOUT_ITEM
127 | LOOP_UNTIL: NEVER
128 | EXECUTE: PICK_UP_ITEM
129 | NON_CRITICAL: True
130 |
131 | [ACTION_FIND_ITEM]
132 | SATISFIES: KNOWS_ABOUT_ITEM
133 | DESIRES: HAS_NON_RELAXED_GOAL
134 | LOOP_UNTIL: HAS_NEEDS_TO_MEET
135 |
136 | [ACTION_GO_TO_SHELTER]
137 | SATISFIES: IS_IN_SHELTER
138 | DESIRES: HAS_SHELTER
139 | EXECUTE: TAKE_SHELTER
140 | LOOP_UNTIL: IS_IN_SHELTER
141 |
142 | [ACTION_FIND_SHELTER]
143 | SATISFIES: HAS_SHELTER
144 | DESIRES: HAS_NON_RELAXED_GOAL
145 | LOOP_UNTIL: HAS_SHELTER
146 |
147 | [ACTION_FOLLOW]
148 | SATISFIES: !HAS_TARGET_TO_FOLLOW,!HAS_TARGET_TO_GUARD
149 | LOOP_UNTIL: NEVER
150 | EXECUTE: FOLLOW_TARGET
151 |
152 | [ACTION_MANAGE_INVENTORY]
153 | SATISFIES: !NEEDS_TO_MANAGE_INVENTORY
154 | EXECUTE: MANAGE_INVENTORY
155 | LOOP_UNTIL: !MANAGE_INVENTORY
156 |
157 | [ACTION_IDLE]
158 | SATISFIES: HAS_NON_RELAXED_GOAL
159 | EXECUTE: NEVER
160 | LOOP_UNTIL: ALWAYS
161 |
162 | [ACTION_ARM_WEAPON]
163 | DESIRES: !NEEDS_TO_MANAGE_INVENTORY
164 | SATISFIES: WEAPON_IS_ARMED
165 | EXECUTE: NEVER
166 | LOOP_UNTIL: NEVER
167 |
168 | [ACTION_SHOOT_THREAT]
169 | DESIRES: !NEEDS_TO_MANAGE_INVENTORY
170 | SATISFIES: !HAS_THREATS
171 | EXECUTE: RANGED_ATTACK
172 | LOOP_UNTIL: NEVER
173 |
174 | [ACTION_MELEE_THREAT]
175 | SATISFIES: !HAS_THREATS,-MELEE_READY,-!RANGED_READY
176 | EXECUTE: MELEE_ATTACK
177 | LOOP_UNTIL: NEVER
178 |
179 | [ACTION_FIND_THREAT]
180 | DESIRES: !WEAPON_IS_ARMED,-HAS_VISIBLE_THREAT
181 | SATISFIES: !HAS_LOST_THREAT
182 | REQUIRE: IS_CONFIDENT
183 | EXECUTE: SEARCH_FOR_THREAT
184 | LOOP_UNTIL: NEVER
185 |
186 | [ACTION_HOLD]
187 | SATISFIES: !HAS_LOST_THREAT
188 | REQUIRE: !IS_CONFIDENT
189 | EXECUTE: NEVER
190 | LOOP_UNTIL: NEVER
191 |
192 | [ACTION_COVER]
193 | SATISFIES: !HAS_VISIBLE_THREAT
194 | EXECUTE: HIDE
195 | LOOP_UNTIL: NEVER
196 |
197 | [ACTION_SAFETY]
198 | SATISFIES: !COVER_EXPOSED
199 | EXECUTE: TAKE_COVER
200 | LOOP_UNTIL: NEVER
201 |
202 | [ACTION_MISSION]
203 | SATISFIES: !HAS_MISSION
204 | EXECUTE: DO_MISSION
205 | LOOP_UNTIL: NEVER
206 |
207 | #END
208 |
--------------------------------------------------------------------------------
/drawing.py:
--------------------------------------------------------------------------------
1 | #All of this code is awful and needs to be re-written at some point.
2 | #The line drawing functions have been reimplemented in other modules,
3 | #so I'm not entirely sure how often these are called.
4 |
5 | class line_diag:
6 | def __init__(self, start, end):
7 | self.path = []
8 | if start == end: return None
9 |
10 | self.start = list(start)
11 | self.end = list(end)
12 |
13 | self.steep = abs(self.end[1]-self.start[1]) > abs(self.end[0]-self.start[0])
14 |
15 | if self.steep:
16 | self.start = self.swap(self.start[0],self.start[1])
17 | self.end = self.swap(self.end[0],self.end[1])
18 |
19 | if self.start[0] > self.end[0]:
20 | self.start[0],self.end[0] = self.swap(self.start[0],self.end[0])
21 | self.start[1],self.end[1] = self.swap(self.start[1],self.end[1])
22 |
23 | dx = self.end[0] - self.start[0]
24 | dy = abs(self.end[1] - self.start[1])
25 | error = 0
26 |
27 | try:
28 | derr = dy/float(dx)
29 | except:
30 | return None
31 |
32 | ystep = 0
33 | y = self.start[1]
34 |
35 | if self.start[1] < self.end[1]: ystep = 1
36 | else: ystep = -1
37 |
38 | for x in range(self.start[0],self.end[0]+1):
39 | if self.steep:
40 | self.path.append((y,x))
41 | else:
42 | self.path.append((x,y))
43 |
44 | error += derr
45 |
46 | if error >= 0.5:
47 | y += ystep
48 | error -= 1.0
49 |
50 | if not self.path[0] == (start[0],start[1]):
51 | self.path.reverse()
52 |
53 | def swap(self,n1,n2):
54 | return [n2,n1]
55 |
56 | def diag_line(start, end):
57 | _l = line_diag(start,end)
58 |
59 | return _l.path
60 |
61 | def draw_3d_line(pos1,pos2):
62 | _xchange = pos2[0]-pos1[0]
63 | _ychange = pos2[1]-pos1[1]
64 | _zchange = pos2[2]-pos1[2]
65 |
66 | _line = [tuple(pos1)]
67 |
68 | if abs(_xchange) >= abs(_ychange) and abs(_xchange) >= abs(_zchange):
69 | _xnegative = False
70 | _ynegative = False
71 | _znegative = False
72 |
73 | if _ychange < 0:
74 | _ynegative = True
75 |
76 | if _xchange < 0:
77 | _xnegative = True
78 |
79 | if _zchange < 0:
80 | _znegative = True
81 |
82 | _x = pos1[0]
83 | _y = pos1[1]
84 | _z = pos1[2]
85 |
86 | try:
87 | _ystep = abs(_ychange/float(_xchange))
88 | except:
89 | _ystep = abs(_ychange)
90 |
91 | try:
92 | _zstep = abs(_zchange/float(_xchange))
93 | except:
94 | _zstep = abs(_zchange)
95 |
96 | for x in range(1,abs(_xchange)):
97 | if _xnegative:
98 | x = -x
99 |
100 | if _ynegative:
101 | _y -= _ystep
102 | else:
103 | _y += _ystep
104 |
105 | if _znegative:
106 | _z -= _zstep
107 | else:
108 | _z += _zstep
109 |
110 | _line.append((_x+x,int(round(_y)),int(round(_z))))
111 |
112 | elif abs(_ychange) >= abs(_xchange) and abs(_ychange) >= abs(_zchange):
113 | _xnegative = False
114 | _ynegative = False
115 | _znegative = False
116 |
117 | if _ychange < 0:
118 | _ynegative = True
119 |
120 | if _xchange < 0:
121 | _xnegative = True
122 |
123 | if _zchange < 0:
124 | _znegative = True
125 |
126 | _x = pos1[0]
127 | _y = pos1[1]
128 | _z = pos1[2]
129 | _xstep = abs(_xchange/float(_ychange))
130 | _zstep = abs(_zchange/float(_ychange))
131 |
132 | for y in range(1,abs(_ychange)):
133 | if _ynegative:
134 | y = -y
135 |
136 | if _xnegative:
137 | _x -= _xstep
138 | else:
139 | _x += _xstep
140 |
141 | if _znegative:
142 | _z -= _zstep
143 | else:
144 | _z += _zstep
145 |
146 | _line.append((int(round(_x)),_y+y,int(round(_z))))
147 |
148 | elif abs(_zchange) > abs(_xchange) and abs(_zchange) > abs(_ychange):
149 | _xnegative = False
150 | _ynegative = False
151 | _znegative = False
152 |
153 | if _zchange < 0:
154 | _znegative = True
155 |
156 | if _xchange < 0:
157 | _xnegative = True
158 |
159 | if _ychange < 0:
160 | _ynegative = True
161 |
162 | _x = pos1[0]
163 | _y = pos1[1]
164 | _z = pos1[2]
165 | _xstep = abs(_xchange/float(_zchange))
166 | _ystep = abs(_ychange/float(_zchange))
167 |
168 | for z in range(1,abs(_zchange)):
169 | if _znegative:
170 | z = -z
171 |
172 | if _xnegative:
173 | _x -= _xstep
174 | else:
175 | _x += _xstep
176 |
177 | if _ynegative:
178 | _y -= _ystep
179 | else:
180 | _y += _ystep
181 |
182 | _line.append((int(round(_x)),int(round(_y)),_z+z))
183 |
184 | _line.append(tuple(pos2))
185 |
186 | return _line
187 |
188 | def draw_circle(at, size):
189 | Circle = 0
190 | width=size
191 | height=size
192 | CenterX=(width/float(2))
193 | CenterY=(height/float(2))
194 | circle = []
195 |
196 | for i in range(height+1):
197 | for j in range(width+1):
198 | Circle = (((i-CenterY)*(i-CenterY))/((float(height)/2)*(float(height)/2)))+((((j-CenterX)*(j-CenterX))/((float(width)/2)*(float(width)/2))));
199 | if Circle>0 and Circle<1.1:
200 | circle.append((at[0]+(j-(width/2)),at[1]+(i-(height/2))))
201 |
202 | if not at in circle:
203 | circle.append(at)
204 |
205 | return circle
--------------------------------------------------------------------------------
/data/life/human.json:
--------------------------------------------------------------------------------
1 | {
2 | "body": {
3 | "lthigh": {
4 | "damage_mod": 1.0,
5 | "bleed_mod": 1.3,
6 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE",
7 | "parent": "hip",
8 | "size": 4
9 | },
10 | "lknee": {
11 | "damage_mod": 1.0,
12 | "bleed_mod": 0.5,
13 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE",
14 | "parent": "lthigh",
15 | "size": 3
16 | },
17 | "rfoot": {
18 | "damage_mod": 1.0,
19 | "bleed_mod": 0.2,
20 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE",
21 | "parent": "rlowerleg",
22 | "size": 2
23 | },
24 | "llowerleg": {
25 | "damage_mod": 1.0,
26 | "bleed_mod": 0.3,
27 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE",
28 | "parent": "lknee",
29 | "size": 3
30 | },
31 | "rhand": {
32 | "damage_mod": 1.0,
33 | "bleed_mod": 0.5,
34 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE",
35 | "parent": "rforearm",
36 | "size": 2
37 | },
38 | "rupperarm": {
39 | "damage_mod": 1.0,
40 | "bleed_mod": 0.3,
41 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE",
42 | "parent": "lshoulder",
43 | "size": 3
44 | },
45 | "lhand": {
46 | "damage_mod": 1.0,
47 | "bleed_mod": 0.5,
48 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE",
49 | "parent": "lforearm",
50 | "size": 2
51 | },
52 | "rthigh": {
53 | "damage_mod": 1.0,
54 | "bleed_mod": 1.3,
55 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE",
56 | "parent": "hip",
57 | "size": 4
58 | },
59 | "rknee": {
60 | "damage_mod": 1.0,
61 | "bleed_mod": 0.5,
62 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE",
63 | "parent": "rthigh",
64 | "size": 3
65 | },
66 | "chest": {
67 | "damage_mod": 2.0,
68 | "bleed_mod": 1.5,
69 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE|CRUCIAL",
70 | "parent": "neck",
71 | "size": 7
72 | },
73 | "lelbow": {
74 | "damage_mod": 1.0,
75 | "bleed_mod": 0.2,
76 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE",
77 | "parent": "lupperarm",
78 | "size": 2
79 | },
80 | "relbow": {
81 | "damage_mod": 1.0,
82 | "bleed_mod": 0.2,
83 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE",
84 | "parent": "rupperarm",
85 | "size": 2
86 | },
87 | "lupperarm": {
88 | "damage_mod": 1.0,
89 | "bleed_mod": 0.3,
90 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE",
91 | "parent": "lshoulder",
92 | "size": 3
93 | },
94 | "hip": {
95 | "damage_mod": 2.0,
96 | "bleed_mod": 1.0,
97 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE|CRUCIAL",
98 | "parent": "stomach",
99 | "size": 4
100 | },
101 | "head": {
102 | "damage_mod": 2.0,
103 | "bleed_mod": 1.5,
104 | "flags": "SKIN[{\"thickness\": 2}]|BONE|CRUCIAL|AFFECTS[sight,hearing]",
105 | "size": 4
106 | },
107 | "stomach": {
108 | "damage_mod": 1.0,
109 | "bleed_mod": 1.5,
110 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE|CRUCIAL",
111 | "parent": "chest",
112 | "size": 5
113 | },
114 | "rlowerleg": {
115 | "damage_mod": 1.0,
116 | "bleed_mod": 0.3,
117 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE",
118 | "parent": "rknee",
119 | "size": 3
120 | },
121 | "neck": {
122 | "damage_mod": 2.0,
123 | "bleed_mod": 2.0,
124 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE|CRUCIAL",
125 | "parent": "head",
126 | "size": 3
127 | },
128 | "lshoulder": {
129 | "damage_mod": 1.0,
130 | "bleed_mod": 0.5,
131 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE",
132 | "parent": "chest",
133 | "size": 4
134 | },
135 | "lforearm": {
136 | "damage_mod": 1.0,
137 | "bleed_mod": 0.2,
138 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE",
139 | "parent": "lelbow",
140 | "size": 3
141 | },
142 | "lfoot": {
143 | "damage_mod": 1.0,
144 | "bleed_mod": 0.2,
145 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE",
146 | "parent": "llowerleg",
147 | "size": 2
148 | },
149 | "rforearm": {
150 | "damage_mod": 1.0,
151 | "bleed_mod": 0.2,
152 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE",
153 | "parent": "relbow",
154 | "size": 3
155 | },
156 | "rshoulder": {
157 | "damage_mod": 1.0,
158 | "bleed_mod": 0.5,
159 | "flags": "SKIN[{\"thickness\": 2}]|MUSCLE|BONE",
160 | "parent": "chest",
161 | "size": 4
162 | }
163 | },
164 | "name": "$FIRST_AND_LAST_NAME_FROM_SPECIES",
165 | "vars": "vision_max=40|hunger_max=20000|thirst_max=10000|hunger=hunger_max|thirst=thirst_max",
166 | "flags": "INTELLIGENT|USES_FIREARMS|LEGS[lthigh,rthigh]|HANDS[lhand,rhand]|ARMS[lshouder,rshoulder]|CAN_GROUP|CAN_SEE|HUNGER|THIRST|MELEE[rfoot,lfoot,lhand,rhand]|RANGED[lhand,rhand]",
167 | "type": "humanoid",
168 | "species": "human",
169 | "icon": "@"
170 | }
--------------------------------------------------------------------------------
/render_map.pyx:
--------------------------------------------------------------------------------
1 | from graphics import blit_tile, darken_tile, blit_char, blit_tile, get_view_by_name, blit_char_to_view
2 | from globals import MAP_CHAR_BUFFER, DARK_BUFFER
3 | from tiles import *
4 |
5 | import libtcodpy as tcod
6 |
7 | import numbers
8 | import effects
9 | import numpy
10 | import tiles
11 | import alife
12 |
13 | import time
14 |
15 | VERSION = 6
16 |
17 | def render_map(map, force_camera_pos=None, view_size=MAP_WINDOW_SIZE, **kwargs):
18 | cdef int _CAMERA_POS[2]
19 | cdef int _MAP_SIZE[2]
20 | cdef int _MAP_WINDOW_SIZE[2]
21 | _CAMERA_POS[0] = CAMERA_POS[0]
22 | _CAMERA_POS[1] = CAMERA_POS[1]
23 | _CAMERA_POS[2] = CAMERA_POS[2]
24 | _MAP_SIZE[0] = MAP_SIZE[0]
25 | _MAP_SIZE[1] = MAP_SIZE[1]
26 | _MAP_SIZE[2] = MAP_SIZE[2]
27 | _MAP_WINDOW_SIZE[0] = view_size[0]
28 | _MAP_WINDOW_SIZE[1] = view_size[1]
29 |
30 | cdef int x, y, z
31 | cdef int _X_MAX = _CAMERA_POS[0]+view_size[0]
32 | cdef int _Y_MAX = _CAMERA_POS[1]+view_size[1]
33 | cdef int _X_START = _CAMERA_POS[0]
34 | cdef int _Y_START = _CAMERA_POS[1]
35 | cdef int _RENDER_X = 0
36 | cdef int _RENDER_Y = 0
37 |
38 | cdef int _min_los_x = 0
39 | cdef int _max_los_x = view_size[0]
40 | cdef int _min_los_y = 0
41 | cdef int _max_los_y = view_size[1]
42 |
43 | cdef int _x_mod = 0
44 | cdef int _y_mod = 0
45 |
46 | if force_camera_pos:
47 | _cam_pos = force_camera_pos[:]
48 | else:
49 | _cam_pos = SETTINGS['camera_track'][:]
50 |
51 | if not CAMERA_POS[0]:
52 | _x_mod = SETTINGS['camera_track'][0]-view_size[0]/2
53 | elif CAMERA_POS[0]+view_size[0]>=MAP_SIZE[0]:
54 | _x_mod = 0
55 |
56 | if not CAMERA_POS[1]:
57 | _y_mod = SETTINGS['camera_track'][1]-view_size[1]/2
58 | elif CAMERA_POS[1]+view_size[1]>=MAP_SIZE[1]:
59 | _y_mod = 0
60 |
61 | _camera_x_offset = CAMERA_POS[0]-_cam_pos[0]
62 | _camera_y_offset = CAMERA_POS[1]-_cam_pos[1]
63 |
64 | los = None
65 | if 'los' in kwargs:
66 | los = kwargs['los']
67 | _min_los_x = (los.shape[0]/2)-view_size[0]/2-_x_mod+_camera_x_offset
68 | _max_los_x = los.shape[0]
69 | _min_los_y = (los.shape[1]/2)-view_size[1]/2-_y_mod+_camera_y_offset
70 | _max_los_y = los.shape[1]
71 |
72 | _view = get_view_by_name('map')
73 | _TEMP_MAP_CHAR_BUFFER = _view['char_buffer'][1].copy()
74 |
75 | if _X_MAX>_MAP_SIZE[0]:
76 | _X_MAX = _MAP_SIZE[0]
77 |
78 | if _Y_MAX>_MAP_SIZE[1]:
79 | _Y_MAX = _MAP_SIZE[1]
80 |
81 | for x in range(_X_START, _X_MAX):
82 | _RENDER_X = x-_CAMERA_POS[0]
83 | for y in range(_Y_START, _Y_MAX):
84 | _RENDER_Y = y-_CAMERA_POS[1]
85 |
86 | if _min_los_x+_RENDER_X<0 or _min_los_x+_RENDER_X>=_max_los_x or _min_los_y+_RENDER_Y<0 or _min_los_y+_RENDER_Y>=_max_los_y:
87 | _visible = False
88 | elif 'los' in kwargs:
89 | _visible = los[_min_los_x+_RENDER_X, _min_los_y+_RENDER_Y]
90 | else:
91 | _visible = True
92 |
93 | if _TEMP_MAP_CHAR_BUFFER[_RENDER_Y,_RENDER_X]:
94 | continue
95 |
96 | _view['light_buffer'][1][_RENDER_Y, _RENDER_X] = 0
97 | _drawn = False
98 | _shadow = 2
99 | for z in range(MAP_SIZE[2]-1,-1,-1):
100 | if map[x][y][z]:
101 | if z > _CAMERA_POS[2] and SETTINGS['draw z-levels above']:
102 | if 'translucent' in tiles.get_raw_tile(tiles.get_tile((x, y, z))):
103 | if _shadow >= 2:
104 | _shadow += 1
105 | else:
106 | _shadow = 0
107 |
108 | if not _visible:
109 | blit_tile(_RENDER_X, _RENDER_Y, map[x][y][z], 'map')
110 | darken_tile(_RENDER_X, _RENDER_Y, abs((_CAMERA_POS[2]-z))*10)
111 | _drawn = True
112 |
113 | elif z == _CAMERA_POS[2]:
114 | if (x,y,z) in SELECTED_TILES[0] and time.time()%1>=0.5:
115 | if time.time()%1>=0.75:
116 | _back_color = tcod.black
117 | else:
118 | _back_color = tcod.white
119 |
120 | blit_char_to_view(_RENDER_X,
121 | _RENDER_Y,
122 | 'X',
123 | (tcod.darker_grey, _back_color),
124 | 'map')
125 | else:
126 | if _shadow > 2:
127 | darken_tile(_RENDER_X, _RENDER_Y, 15*(_shadow-2))
128 |
129 | if map[x][y][z+1]:
130 | blit_tile(_RENDER_X, _RENDER_Y, map[x][y][z+1], 'map')
131 | else:
132 | blit_tile(_RENDER_X, _RENDER_Y, map[x][y][z], 'map')
133 |
134 | if SETTINGS['draw effects']:
135 | if _visible:
136 | effects.draw_splatter((x,y,z), (_RENDER_X,_RENDER_Y))
137 | effects.draw_effect((x, y))
138 |
139 | if not _visible:
140 | darken_tile(_RENDER_X, _RENDER_Y, 30)
141 |
142 | if SETTINGS['draw visible chunks']:
143 | _visible_chunks = alife.brain.get_flag(LIFE[SETTINGS['controlling']], 'visible_chunks')
144 |
145 | if _visible_chunks:
146 | for _chunk in _visible_chunks:
147 | if alife.chunks.position_is_in_chunk((x, y), _chunk):
148 | darken_tile(_RENDER_X, _RENDER_Y, abs(90))
149 |
150 | _drawn = True
151 |
152 | elif z < _CAMERA_POS[2] and SETTINGS['draw z-levels below']:
153 | blit_tile(_RENDER_X,_RENDER_Y,map[x][y][z], 'map')
154 | darken_tile(_RENDER_X,_RENDER_Y,abs((_CAMERA_POS[2]-z))*10)
155 | _drawn = True
156 |
157 | if SETTINGS['draw z-levels above'] and _drawn:
158 | break
159 |
160 | if not _drawn:
161 | blit_tile(_RENDER_X, _RENDER_Y, BLANK_TILE, 'map')
162 |
--------------------------------------------------------------------------------