From DoomWiki.org

< User:Fraggle
Revision as of 19:35, 22 March 2020 by Fraggle (talk | contribs) (Created page with "This is the Python script I wrote to generate the pages under Category:Code pointers. It uses DEH 9000. <pre> #!/usr/bin/env python import deh9000 import fns TEMPLA...")

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

This is the Python script I wrote to generate the pages under Category:Code pointers. It uses DEH 9000.

#!/usr/bin/env python

import deh9000
import fns

TEMPLATE = """{{DISPLAYTITLE:%(ptrname)s}}
{{stub}}
'''%(ptrname)s''' is a [[code pointer]] used in %(purpose)s.

== Uses ==

The <code>%(ptrname)s</code> code pointer appears in the following [[state|states]] in Doom's state table:

%(uses_table)s

== Example ==

The following is an example of how to set the <code>%(ptrname)s</code> code pointer in a Dehacked file:

%(example)s

Or using [[BEX]] syntax:

 [CODEPTR]
 Frame 1234 = %(bexname)s

== External links ==
* {{DoomSrc|file=%(filename)s|line=%(lineno)s|text=%(ptrname)s}} in the Doom source code.

[[Category:Code pointers]]
"""

seq_descriptions = {
        "upstate": "weapon raise",
	"downstate": "weapon lower",
	"readystate": "normal weapon",
	"atkstate": "attack",
	"flashstate": "gun flash",

	"spawnstate": "normal",
	"seestate": "normal",
	"painstate": "pain",
	"meleestate": "melee attack",
	"missilestate": "attack",
	"deathstate": "death",
	"xdeathstate": "explode",
	"raisestate": "respawn",
}

def wikitable(rows):
	result = [
		"{| {{prettytable|classes=plainlinks}}",
		"|-",
	]
	for idx, row in enumerate(rows):
		if idx == 0:
			start = "! "
		else:
			start = "| "
		result.append(start + (" || ".join(row)))
		result.append("|-")
	result.append("|}")
	return "\n".join(result)

def describe_obj(obj):
	return "[[%s]]" % (obj.object_name)

def x_y_and_z(items):
	items = list(items)

	if len(items) == 0:
		return "nothing?"
	elif len(items) == 1:
		return items[0]
	elif len(items) == 2:
		return "%s and %s" % (items[0], items[1])

	rest, last = items[:-1], items[-1]

	return "%s and %s" % (
		" ".join("%s," % x for x in rest),
		last,
	)

def is_monster(obj):
	really_monsters = [
		deh9000.mobjinfo[deh9000.MT_BARREL],
		deh9000.mobjinfo[deh9000.MT_SKULL],
		deh9000.mobjinfo[deh9000.MT_KEEN],
		deh9000.mobjinfo[deh9000.MT_BOSSSPIT],
	]
	return obj in really_monsters or (
		isinstance(obj, deh9000.mobjinfo_t) and
		(obj.flags & deh9000.MF_COUNTKILL) != 0
	)

def describe_objs(objs):
	if len(objs) <= 5:
		result = "the " + x_y_and_z(describe_obj(x) for x in objs)
		return result.replace("the [[Commander", "[[Commander", 1)

	# Too many to list - it will become a tedious read.
	if all(is_monster(x) for x in objs):
		return "various monsters"
	if all(x == deh9000.mobjinfo[deh9000.MT_PLAYER] or
	       is_monster(x) for x in objs):
		return "the [[Player]] and various monsters"
	if all(w in objs for w in deh9000.weaponinfo):
		return "all weapons"
	if all(isinstance(x, deh9000.weaponinfo_t) for x in objs):
		return "various weapons"
	return "various objects"

def describe_seqs(seqs):
	return x_y_and_z(seq_descriptions[x] for x in seqs)

def describe_seq_set(seqset):
	objs = set()
	seqs = set()
	for obj, seq in seqset:
		objs.add(obj)
		seqs.add(seq)
	if len(objs) > 1 and len(seqs) > 2:
		return "animation sequences for %s" % (	
			describe_objs(objs),
		)
	return "the %s %s for %s" % (
		describe_seqs(seqs),
		"animation" if len(seqs) == 1 else "animations",
		describe_objs(objs),
	)

def process_obj(obj):
	states = {}
	for seq in obj.state_fields:
		start = getattr(obj, seq)
		for idx, state_id in enumerate(deh9000.states.walk(start)):
			new = (idx, seq)
			cur = states.get(state_id, new)
			states[state_id] = min(cur, new)

	for state_id, (_, seq) in states.items():
		state = deh9000.states[state_id]
		state_uses[state_id].add((obj, seq))
		if state.action is not None:
			uses = ptrs.setdefault(state.action, set())
			uses.add((obj, seq))

def state_link(state_id):
	return "{{DoomSrc|text=%s|file=info.c|line=%d}}" % (
		str(deh9000.statenum_t[state_id]),
		state_id + 136,
	)

def ptr_frame_list(ptr):
	result = [
		("Frame number", "Doom internal name", "Use"),
	]
	for state_id, state in enumerate(deh9000.states):
		if state.action != ptr:
			continue
		description = describe_seq_set(state_uses[state_id])
		if description.startswith("the "):
			description = description[4:]
		result.append((
			"%d" % state_id,
			state_link(state_id),
			description,
		))
	return result

def example_patch(ptr):
	dehfile = deh9000.DehackedFile()
	state_id = deh9000.S_DSGUNUP
	while dehfile.states[state_id].action in (None, ptr):
		state_id += 1
	dehfile.states[state_id].action = ptr
	diff = dehfile.dehacked_diffs()[-1]
	return "\n".join(" "+x for x in diff.split("\n"))


ptrs = {}
state_uses = [set() for _ in deh9000.states]
for obj in deh9000.mobjinfo:
	process_obj(obj)
for obj in deh9000.weaponinfo:
	process_obj(obj)

for ptr, uses in sorted(ptrs.items()):
	purpose = describe_seq_set(uses)
	ptrname = str(ptr)
	filename, lineno = fns.FUNCTIONS[ptrname]
	txt = TEMPLATE % dict(
		ptrname=ptrname,
		bexname=ptrname[2:],
		purpose=purpose,
		filename=filename,
		lineno=lineno,
		uses_table=wikitable(ptr_frame_list(ptr)),
		example=example_patch(ptr),
	)
	with open("codeptrs/%s.txt" % ptrname, "w") as f:
		f.write(txt)