Factorio: add DeathLink option
This commit is contained in:
parent
c152790011
commit
4472ef20fe
|
@ -5,6 +5,7 @@ import asyncio
|
|||
import urllib.parse
|
||||
import sys
|
||||
import os
|
||||
import typing
|
||||
|
||||
import websockets
|
||||
|
||||
|
@ -91,6 +92,7 @@ class ClientCommandProcessor(CommandProcessor):
|
|||
|
||||
|
||||
class CommonContext():
|
||||
tags:typing.Set[str] = {"AP"}
|
||||
starting_reconnect_delay: int = 5
|
||||
current_reconnect_delay: int = starting_reconnect_delay
|
||||
command_processor: int = ClientCommandProcessor
|
||||
|
@ -489,7 +491,10 @@ if __name__ == '__main__':
|
|||
# Text Mode to use !hint and such with games that have no text entry
|
||||
init_logging("TextClient")
|
||||
|
||||
|
||||
class TextContext(CommonContext):
|
||||
tags = {"AP", "IgnoreGame"}
|
||||
|
||||
async def server_auth(self, password_requested: bool = False):
|
||||
if password_requested and not self.password:
|
||||
await super(TextContext, self).server_auth(password_requested)
|
||||
|
@ -499,10 +504,11 @@ if __name__ == '__main__':
|
|||
|
||||
await self.send_msgs([{"cmd": 'Connect',
|
||||
'password': self.password, 'name': self.auth, 'version': Utils.version_tuple,
|
||||
'tags': ['AP', 'IgnoreGame'],
|
||||
'tags': self.tags,
|
||||
'uuid': Utils.get_unique_identifier(), 'game': self.game
|
||||
}])
|
||||
|
||||
|
||||
async def main(args):
|
||||
ctx = TextContext(args.connect, args.password)
|
||||
ctx.server_task = asyncio.create_task(server_loop(ctx), name="ServerLoop")
|
||||
|
|
|
@ -54,10 +54,12 @@ class FactorioContext(CommonContext):
|
|||
|
||||
def __init__(self, server_address, password):
|
||||
super(FactorioContext, self).__init__(server_address, password)
|
||||
self.send_index = 0
|
||||
self.send_index: int = 0
|
||||
self.rcon_client = None
|
||||
self.awaiting_bridge = False
|
||||
self.write_data_path = None
|
||||
self.last_death_link: float = time.time() # last send/received death link on AP layer
|
||||
self.death_link_tick: int = 0 # last send death link on Factorio layer
|
||||
self.factorio_json_text_parser = FactorioJSONtoTextParser(self)
|
||||
|
||||
async def server_auth(self, password_requested: bool = False):
|
||||
|
@ -76,13 +78,13 @@ class FactorioContext(CommonContext):
|
|||
'password': self.password,
|
||||
'name': self.auth,
|
||||
'version': Utils.version_tuple,
|
||||
'tags': ['AP'],
|
||||
'tags': self.tags,
|
||||
'uuid': Utils.get_unique_identifier(),
|
||||
'game': "Factorio"
|
||||
}])
|
||||
|
||||
def on_print(self, args: dict):
|
||||
logger.info(args["text"])
|
||||
super(FactorioContext, self).on_print(args)
|
||||
if self.rcon_client:
|
||||
self.print_to_game(args['text'])
|
||||
|
||||
|
@ -107,6 +109,12 @@ class FactorioContext(CommonContext):
|
|||
self.rcon_client.send_commands({item_name: f'/ap-get-technology ap-{item_name}-\t-1' for
|
||||
item_name in args["checked_locations"]})
|
||||
|
||||
elif cmd == "Bounced":
|
||||
if self.rcon_client:
|
||||
tags = args.get("tags", [])
|
||||
if "DeathLink" in tags and self.last_death_link != args["data"]["time"]:
|
||||
self.rcon_client.send_command(f"/ap-deathlink {args['data']['source']}")
|
||||
|
||||
|
||||
async def game_watcher(ctx: FactorioContext):
|
||||
bridge_logger = logging.getLogger("FactorioWatcher")
|
||||
|
@ -139,6 +147,17 @@ async def game_watcher(ctx: FactorioContext):
|
|||
f"{[lookup_id_to_name[rid] for rid in research_data - ctx.locations_checked]}")
|
||||
ctx.locations_checked = research_data
|
||||
await ctx.send_msgs([{"cmd": 'LocationChecks', "locations": tuple(research_data)}])
|
||||
death_link_tick = data.get("death_link_tick", 0)
|
||||
if death_link_tick != ctx.death_link_tick:
|
||||
ctx.death_link_tick = death_link_tick
|
||||
ctx.last_death_link = time.time()
|
||||
await ctx.send_msgs([{
|
||||
"cmd": "Bounce", "tags": ["DeathLink"],
|
||||
"data": {
|
||||
"time": ctx.last_death_link,
|
||||
"source": ctx.player_names[ctx.slot]
|
||||
}
|
||||
}])
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
except Exception as e:
|
||||
|
@ -227,6 +246,10 @@ def get_info(ctx, rcon_client):
|
|||
info = json.loads(rcon_client.send_command("/ap-rcon-info"))
|
||||
ctx.auth = info["slot_name"]
|
||||
ctx.seed_name = info["seed_name"]
|
||||
# 0.2.0 addition, not present earlier
|
||||
death_link = bool(info.get("death_link", False))
|
||||
if death_link:
|
||||
ctx.tags.add("DeathLink")
|
||||
|
||||
|
||||
async def factorio_spinup_server(ctx: FactorioContext) -> bool:
|
||||
|
|
|
@ -122,7 +122,7 @@ class Context(CommonContext):
|
|||
auth = base64.b64encode(self.rom).decode()
|
||||
await self.send_msgs([{"cmd": 'Connect',
|
||||
'password': self.password, 'name': auth, 'version': Utils.version_tuple,
|
||||
'tags': get_tags(self),
|
||||
'tags': self.tags,
|
||||
'uuid': Utils.get_unique_identifier(), 'game': "A Link to the Past"
|
||||
}])
|
||||
|
||||
|
@ -705,12 +705,6 @@ async def snes_flush_writes(ctx: Context):
|
|||
await snes_write(ctx, writes)
|
||||
|
||||
|
||||
# kept as function for easier wrapping by plugins
|
||||
def get_tags(ctx: Context):
|
||||
tags = ['AP']
|
||||
return tags
|
||||
|
||||
|
||||
async def track_locations(ctx: Context, roomid, roomdata):
|
||||
new_locations = []
|
||||
|
||||
|
|
|
@ -489,7 +489,7 @@ async def on_client_connected(ctx: Context, client: Client):
|
|||
'cmd': 'RoomInfo',
|
||||
'password': bool(ctx.password),
|
||||
'players': players,
|
||||
'games': [ctx.games[x] for x in range(1, len(ctx.games)+1)],
|
||||
'games': [ctx.games[x] for x in range(1, len(ctx.games) + 1)],
|
||||
# tags are for additional features in the communication.
|
||||
# Name them by feature or fork, as you feel is appropriate.
|
||||
'tags': ctx.tags,
|
||||
|
@ -1245,7 +1245,6 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict):
|
|||
|
||||
await ctx.send_msgs(client, reply)
|
||||
|
||||
|
||||
elif cmd == "GetDataPackage":
|
||||
exclusions = set(args.get("exclusions", []))
|
||||
if exclusions:
|
||||
|
|
|
@ -73,7 +73,7 @@ def generate_mod(world, output_directory: str):
|
|||
random = multiworld.slot_seeds[player]
|
||||
|
||||
def flop_random(low, high, base=None):
|
||||
"""Guarentees 50% bwlo base and 50% above base, uniform distribution in each direction."""
|
||||
"""Guarentees 50% below base and 50% above base, uniform distribution in each direction."""
|
||||
if base:
|
||||
distance = random.random()
|
||||
if random.randint(0, 1):
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from __future__ import annotations
|
||||
import typing
|
||||
|
||||
from Options import Choice, OptionDict, ItemDict, Option, DefaultOnToggle, Range
|
||||
from Options import Choice, OptionDict, ItemDict, Option, DefaultOnToggle, Range, Toggle
|
||||
from schema import Schema, Optional, And, Or
|
||||
|
||||
# schema helpers
|
||||
|
@ -284,6 +284,11 @@ class ImportedBlueprint(DefaultOnToggle):
|
|||
displayname = "Blueprints"
|
||||
|
||||
|
||||
class DeathLink(Toggle):
|
||||
"""When you die, everyone dies. Of course the reverse is true too."""
|
||||
displayname = "Death Link"
|
||||
|
||||
|
||||
factorio_options: typing.Dict[str, type(Option)] = {
|
||||
"max_science_pack": MaxSciencePack,
|
||||
"tech_tree_layout": TechTreeLayout,
|
||||
|
@ -300,4 +305,5 @@ factorio_options: typing.Dict[str, type(Option)] = {
|
|||
"evolution_traps": EvolutionTrapCount,
|
||||
"attack_traps": AttackTrapCount,
|
||||
"evolution_trap_increase": EvolutionTrapIncrease,
|
||||
"death_link": DeathLink
|
||||
}
|
||||
|
|
|
@ -8,6 +8,9 @@ SLOT_NAME = "{{ slot_name }}"
|
|||
SEED_NAME = "{{ seed_name }}"
|
||||
FREE_SAMPLE_BLACKLIST = {{ dict_to_lua(free_sample_blacklist) }}
|
||||
TRAP_EVO_FACTOR = {{ evolution_trap_increase }} / 100
|
||||
DEATH_LINK = {{ death_link | int }}
|
||||
|
||||
CURRENTLY_DEATH_LOCK = 0
|
||||
|
||||
{% if not imported_blueprints -%}
|
||||
function set_permissions()
|
||||
|
@ -57,6 +60,7 @@ function on_force_created(event)
|
|||
local data = {}
|
||||
data['earned_samples'] = {{ dict_to_lua(starting_items) }}
|
||||
data["victory"] = 0
|
||||
data["death_link_tick"] = 0
|
||||
global.forcedata[event.force] = data
|
||||
{%- if silo == 2 %}
|
||||
check_spawn_silo(force)
|
||||
|
@ -250,6 +254,17 @@ function chain_lookup(table, ...)
|
|||
return table
|
||||
end
|
||||
|
||||
function kill_players(force)
|
||||
CURRENTLY_DEATH_LOCK = 1
|
||||
local current_character = nil
|
||||
for _, player in ipairs(force.players) do
|
||||
current_character = player.character
|
||||
if current_character ~= nil then
|
||||
current_character.die()
|
||||
end
|
||||
end
|
||||
CURRENTLY_DEATH_LOCK = 0
|
||||
end
|
||||
|
||||
function spawn_entity(surface, force, name, x, y, radius, randomize, avoid_ores)
|
||||
local prototype = game.entity_prototypes[name]
|
||||
|
@ -351,6 +366,20 @@ function spawn_entity(surface, force, name, x, y, radius, randomize, avoid_ores)
|
|||
end
|
||||
|
||||
|
||||
if DEATH_LINK == 1 then
|
||||
script.on_event(defines.events.on_entity_died, function(event)
|
||||
if CURRENTLY_DEATH_LOCK == 1 then -- don't re-trigger on same event
|
||||
return
|
||||
end
|
||||
|
||||
local force = event.entity.force
|
||||
global.forcedata[force.name].death_link_tick = game.tick
|
||||
dumpInfo(force)
|
||||
kill_players(force)
|
||||
end, {LuaEntityDiedEventFilter = {["filter"] = "name", ["name"] = "character"}})
|
||||
end
|
||||
|
||||
|
||||
-- add / commands
|
||||
commands.add_command("ap-sync", "Used by the Archipelago client to get progress information", function(call)
|
||||
local force
|
||||
|
@ -363,7 +392,8 @@ commands.add_command("ap-sync", "Used by the Archipelago client to get progress
|
|||
local data_collection = {
|
||||
["research_done"] = research_done,
|
||||
["victory"] = chain_lookup(global, "forcedata", force.name, "victory"),
|
||||
}
|
||||
["death_link_tick"] = chain_lookup(global, "forcedata", force.name, "death_link_tick")
|
||||
}
|
||||
|
||||
for tech_name, tech in pairs(force.technologies) do
|
||||
if tech.researched and string.find(tech_name, "ap%-") == 1 then
|
||||
|
@ -442,7 +472,7 @@ end)
|
|||
|
||||
|
||||
commands.add_command("ap-rcon-info", "Used by the Archipelago client to get information", function(call)
|
||||
rcon.print(game.table_to_json({["slot_name"] = SLOT_NAME, ["seed_name"] = SEED_NAME}))
|
||||
rcon.print(game.table_to_json({["slot_name"] = SLOT_NAME, ["seed_name"] = SEED_NAME, ["death_link"] = DEATH_LINK}))
|
||||
end)
|
||||
|
||||
|
||||
|
@ -453,5 +483,13 @@ end)
|
|||
{% endif -%}
|
||||
|
||||
|
||||
commands.add_command("ap-deathlink", "Kill all players", function(call)
|
||||
local force = game.forces["player"]
|
||||
local source = call.parameter or "Archipelago"
|
||||
kill_players(force)
|
||||
game.print("Death was granted by " .. source)
|
||||
end)
|
||||
|
||||
|
||||
-- data
|
||||
progressive_technologies = {{ dict_to_lua(progressive_technology_table) }}
|
||||
|
|
Loading…
Reference in New Issue