## Anything I can do?

Rising ourselves by our own hair.
broxen
Posts: 60
Joined: Tue Jun 07, 2016 8:26 am
Location: USA
Contact:

### Re: Anything I can do?

OK, bear with me... While I used to write scripts for UOX, develop in PHP, and study C++ in school, I haven't actually coded anything in over 10yrs. To say I'm rusty is an understatement--I'm a total rookie at this point. That said, I'd like to help, so maybe with some guidance...

Here's what I've got so far for Officer Flint, in LUA as that seems popular as a scripting language for games and has significant resources on the web for reference. There are two things to note when reviewing these files: (1) there's still a non-trivial amount of psuedo-code, as I haven't fleshed out the structure for dialog system and player class. (2) my motivation is to script as much as possible to provide maximum flexibility to future server-owners.

Dialog strings were culled from misc.pigg *.MS and *.NPC files where possible. Also, I realize that I'm calling a lot of variables without their table prefix.

contacts.lua -- defines our contact class and helper functions

Code: Select all

--[[
Contacts.lua
Contacts Module defining generic contacts and related functions

TODO: Extend from broader NPC Class, define more defaults
--]]

-- Language Localisation Tables
local en_dialog = {
K1493134086 =	"HelloString",
K234187279	= "You should go see {LastContactName} now, {HeroName}. There's nothing more you can do for me.",
K2433540917	= "Click on <A HREF=CONTACTLINK_MISSIONS>ask about available missions</A> to proceed.",
K348547894  = "IDontKnowYou",
}

-- Contact Class and Prototype
local p = PlayerClass(player)
local client = Client(clientclass)

local Contact = {}

-- NOTE: it's not advisable to define nil-value variable keys in LUA Tables
local Contact.prototype {
model = "MaleNPC",
name = "Generic_NPC",
displayname = "Generic NPC",
costume, -- future use: reference .costume file for npc
location = {map = "map.outbreak",x = 0,y = 0,z = 0}, -- y is the elevation
piggcategory, -- = nil is implicit
piggnpc,
group,
morality = "Heroes",
idle = "PL_StandStill", -- idle animation
about = self:i18n("K2475752812"), -- pass the key, not the variable
-- Variables pulled from npcs_client.bin xml schema
supergroupcontact,
ai,
registersupergroup,
storecount,
store,
cantailor,
canrespec,
canauction,
shoutdialog,
shoutfrequency,
shoutvariation,
autorewards,
talkstostrangers,
visionphases,
exclusivevisionphase,
wrongalliancestring,
-- Dialog Table with Localisation
dialog = {
en = en_dialog, -- English Localisation
fr = fr_dialog, -- French Localisation
de = de_dialog, -- German Localisation
es = es_dialog, -- Spanish Localisation
},
}

local function private()
print("private function example")
end

function Contact:i18n(s)
-- Append two letter localisation code to variable name based upon user config
s = self.dialog .. "." .. client:LoadSettings("lang") .. "." .. s
return s
end

-- Function to define new Contact
function Contact:new(o)
o = o or {}   -- create object if user does not provide one
setmetatable(o, self)
self.__index = self
return o
end

return Contact
Contact_OfficerFlint.lua -- Officer Flint himself

Code: Select all

--[[
Contact_OfficerFlint.lua
Officer Flint from the Outbreak Tutorial Mission for Heroes

misc.pigg\text\English\Contacts\Tutorial\Officer_Flint.npc.ms
K194545998 = "Officer Flint" -- matches P-string from misc.pigg

Mined from npcs_client.bin:
SCRIPTS.LOC/CONTACTS/TUTORIAL/OFFICER_FLINT.NPC
Model_OfficerFlint
Officer_Flint
P194545998 -- matches K-string from misc.pigg
PL_StandStill
Contacts/Tutorial/Officer_Flint.contact
--]]

-- Player and Client objects
local p = PlayerClass(player)
local client = Client(clientclass)

-- Include Contact class
local Contact = require "contacts"

-- Define English (EN) dialog
-- From misc.pigg\text\English\Contacts\Tutorial\Officer_Flint.contact.ms
local en_dialog = {
K1493134086 =	"HelloString",
K1898059956	= "Outbreak",
K194545998	= "Officer Flint",
K1967244823	= "Traffic Cop",
K2060623986	= "<A HREF=CONTACTLINK_INTRODUCE>Officer Parks</A> radioed. He needs help fighting off some rioting thugs down the street.",
K234187279	= "You should go see {LastContactName} now, {HeroName}. There's nothing more you can do for me.",
K2433540917	= "Click on <A HREF=CONTACTLINK_MISSIONS>ask about available missions</A> to proceed.",
K2475752812	= "Officer Flint has simple duties in in the Paragon City Police Department, but, when a crisis arises, he's always first to volunteer for extra duty. His friendly nature and good heart make him an ideal contact for new heroes learning their way around Paragon City.",
K348547894  = "IDontKnowYou",
K3871256239	= "We have a crisis going on here, and we need help desperately. Some thugs took an experimental drug thinking it was something else, and now they're trashing the area. We have to regain control to ensure the saftey of the citizens.",
}

-- Add this NPC to the contacts table
c = Contact:new{
model = "Model_OfficerFlint",
name = "Officer_Flint",
displayname = "Officer Flint",
location = {map = "map.outbreak",x = -61,y = -0,z = 161}, -- y is the elevation
piggcategory = "P1344310001", -- culled from mission architect definition
piggnpc = "P194545998", -- culled from misc.pigg
group = "Tutorial",
morality = "Heroes",
idle = "PL_StandStill", -- idle animation
about = self.i18n("K2475752812"), -- pass the key, not the variable
dialog = {
en = en_dialog, -- Allow script localisation
--    fr = fr_dialog,
--    de = de_dialog,
--    es = es_dialog,
},
storyarcs = {
K1898059956	= "Outbreak",
},
K3928824237	= "Deliver the serum to Dr. Miller",
}
}

-- The Antidote
-- TODO: Move to it's own mission lua file, with logic for dialog tree
K1617305617	= "We need an antidote for these berserk thugs. I've collected this blood sample. <A HREF={ACCEPT}>Please take it to Dr. Miller</A>; he's marked on your map and compass.",
K2241903306	= "Blood sample",
K312082289	= "Excellent job. What did you call yourself again? Was it {HeroName}? That's pretty original. You seem to navigate well, so let me tell you about the Map. Click the Map Button to bring up an overhead view of the zone.<br><br><img src=Tut_Menu_Map.tga><br><br> Click on any item on the map to target it. The target appears on your compass.",
K2673347275	= "Excellent job. What did you call yourself again? Was it {HeroName}? That's pretty original. You seem to navigate well, so let me tell you about the Map. Click the Map Button <img src=Tut_Menu_Map.tga> to bring up an overhead view of the zone. Click on any item on the map to target it. The target appears on your compass.",
K1114271724	= "You should read the plaque in front of Preston Hospital, right next to Dr. Miller.",
K300190428	= "Thank you! This sample will help our research immensely.<br><br>Since you are new to Paragon City, you should familiarize yourself with the way hospitals work here. There is a plaque in front of Preston Hospital next to me you need to read. You can then return to Officer Flint for more instructions.",
K3316883996	= "The blood sample has been placed in your Clue Bag",
K3637128862	= "A blood sample that must be delivered to Dr. Miller.",
K3647315663	= "Return to Officer Flint for more duties",
K3928824237	= "Deliver the serum to Dr. Miller",
K511052023	= "Dr. Miller is just down the street. Give him the sample, and see if he has anything further for you.",
K644472974	= "Read the plaque",
K934716567	= "Use the compass at the top of your screen to find the hospital. Deliver the serum, then see if he has anything more for you.",
}

modal.tutorial = {
tut_nav   = "The Navigation Window is at the top of your screen. <img src=Tut_Menu_Nav.tga>",
tut_clue  = "You have been issued a clue. You can learn more about the Clue by clicking the Clues botton on your Nav window. Whenever you are issued new Clues, you can read more about them there as well.",
}

-- TODO: Move to global Dialog Class, which will be referenced by Contact:dialog()
CONTACTLINK_WHATELSE = "Talk about what else is going on",
MODAL_OK = "OK",
}

-- Move MODAL_WELCOME to some kind of new player start lua file.
MODAL_WELCOME = "Welcome to City of Heroes!\nThis short tutorial will go over the basics of gameplay in order to get you started on your path to becoming one of the greatest heroes in Paragon City.\n\nTo get started, walk up to Officer Flint and talk to him. Talking is as simple as left clicking on the person you would like to talk to.\n\nTo move, simply use the keyboard as illustrated here.\n\nUI Wasd.jpg\n\nHolding the right mouse button down and moving the mouse left and right allows you to turn. When you have familiarized yourself with the controls, click OK in the bottom of this dialog box to close it.",

-- Begin with code new player start. This logic should go in another file
-- specifically for New Player Spawns
do
p:Spawn()

-- Call Modal() to show popup.
-- Welcome & WASD Configuration Message
client:Modal(MODAL_WELCOME,buttons{MODAL_OK})

-- Assign starting storyarc and mission
p:AssignStoryArc(K1898059956) -- K1898059956 = "Outbreak"

-- set active contact and current waypoint
p:SetActiveContact(c.name) -- Officer Flint
p:SetWaypoint(c.location) -- Officer Flint's location
end

-- c:Dialog should inherit from Contact class, which should inherit from a Dialog class
function c:Dialog(d)
repeat
-- TODO: Rewrite as lookup table?
if p:HasStoryArc(K1898059956) then
if d:state == "intro" then
msg = { K3871256239, d:style(K2433540917, "blue") }
-- dialog class has logic for handling links and advancing dialog state
elseif d:state == "briefing" then
msg = { K1617305617, d:style(tut_nav, "blue"), d:style(tut_clue, "blue")}
-- dialog class has logic for handling links and advancing dialog state
elseif d:state == "acceptance" then
p:AwardClue(K2241903306,K3637128862,K3316883996) -- AwardClue(cluename,description,message)
msg = { K511052023 }
-- dialog class has logic for handling links and advancing dialog state
elseif d:state == "solicitation" then
msg = { K934716567 }
-- dialog class has logic for handling links and advancing dialog state
else
-- nothing else to say, so direct user back to next contact
msg = { K234187279 }
end
-- Feed variables and show modal box
-- modal has logic for handling links and advancing dialog state
client:Modal(msg,ln,state)
else
p:AssignStoryArc(K1898059956) -- K1898059956 = "Outbreak"
d:state = "intro"
end
until d:state == "leave"
end
Let me know your thoughts here.
If I'm completely off-base here, please let me know. I don't want to derail any existing progress.

And feel free to provide direction, or examples of what you'd rather see.

Some necessary functions, as I currently see it:

Code: Select all

-- p = player
p:Spawn(player) -- spawn a new player
p:AssignStoryArc(arc,is_active) -- I can't remember if you are allowed multiple storyarcs at one time
p:HasStoryArc(arc)  -- also returns active state
p:AssignTask(task,is_active) -- I can't remember if you are allowed multiple tasks at one time
p:SetWaypoint(wp)
p:GetWaypoint(wp)
p:AwardClue(clue,description,message)
p:HasClue(clue)
p:AssignContact(contact,is_active)
p:HasContact(contact) -- also returns active state
p:AwardXP(xp)
p:AwardDebt(debt)
p:AwardSouvenir(souvenir)
-- c = contact
c = Contact:new{} -- use LUA sugar to call function with single table as only argument
c:Dialog(event) -- or maybe c:OnDialog(d), maybe pulls from Dialog class
c:OnAssigned(event) -- when assigned to player
c:i18n(s) -- localisation shiv
c:Prerequisites(player) -- checks contacts prereqs against player

Some variables

Code: Select all

p.Level -- Current Level
p.XP -- Current XP
p.Debt -- Current Debt
p.Costume -- Costume Value, could be key reference or dictionary table (array)
-- you get the point.

-broxen

broxen
Posts: 60
Joined: Tue Jun 07, 2016 8:26 am
Location: USA
Contact:

### Re: Anything I can do?

I'd like to mention that there doesn't seem to be any correlation between the different *.MS and *.NPC files in misc.pigg, other than an inferred tie between the P string in the NPC file with the K string in the associated MS file.

I said as much in the comments of Officer Flint's Lua script.

Next to that, there doesn't even seem to be a reference tying the contact.ms file to any specific task.ms files. In fact, the task files seem to include text strings for the entire storyarcs, including parts spoken by other NPCs along the way, and clue or waypoint text.

For these reasons the K strings and key values seem completely arbitrary, and it may be better to simply construct our tables without named keys--though perhaps harder to reference later in mission scripts... If keys are kept, we should dictate some sort of consistent naming schema, for code readability.

Also, as an aside, storyarc text and dialog trees should be broken out into their own script, as it's possible or even probable that the same mission may be assigned by different NPCs, without any changes to text.
-broxen

nemerle
Posts: 397
Joined: Thu Jan 10, 2013 3:40 pm

### Re: Anything I can do?

Great work broxen, I'll look into integrating a scripting language this weekend. ( it'll be either lua or chai-script )

As for those 'K******' strings, I'm 90% sure that those numbers are actualy hash values of the mapped strings, used by the original map-server to ease the translation part. So that given a quest pseudo-script:

Code: Select all

...
if(condition)
send_player_text(TRANSLATABLE("You cannot do this"));
else
send_player_text(TRANSLATABLE("You can do this"));
...

some tool will parse the quest script, extract all TRANSLATABLE strings to ease the translator's work.
"Ich was in one sumere dale,
in one suthe diyhele hale,
iherde ich holde grete tale
an hule and one niyhtingale."

broxen
Posts: 60
Joined: Tue Jun 07, 2016 8:26 am
Location: USA
Contact:

### Re: Anything I can do?

Right. That's what I figured too, and came up with a similar solution here:

Code: Select all

function Contact:i18n(s)
-- Append two letter localisation code to variable name based upon user config
s = self.dialog .. "." .. client:LoadSettings("lang") .. "." .. s
return s
end

But that could easily be handled server side.

EDIT: Come to think of it, the proposed dialog class should probably handle this on every dialog string, so that the function doesn't need to be called at all.
-broxen

nemerle
Posts: 397
Joined: Thu Jan 10, 2013 3:40 pm

### Re: Anything I can do?

Translations will be likely handled c++-side using Qt localization support, and LUA will get access to tr('localized string') function.

The per-user configuration will be used to select proper QTranslator instance and the string will be processed by using the loaded translation table.

Also, initial LUA support is getting ready - for now a single interpreter instance is bound to a single MapInstance, and a dummy c++ Contact type is registered in LUA.
Next step will be to add a simple lua evaluation support to the chat : /lua 'lua code to run'
"Ich was in one sumere dale,
in one suthe diyhele hale,
iherde ich holde grete tale
an hule and one niyhtingale."

broxen
Posts: 60
Joined: Tue Jun 07, 2016 8:26 am
Location: USA
Contact:

### Re: Anything I can do?

This is great!

I had trouble getting the server to recognize my client, but I'll try to compile the latest again this weekend.
-broxen

nemerle
Posts: 397
Joined: Thu Jan 10, 2013 3:40 pm

### Re: Anything I can do?

That's strange, can you perhaps verify that your client is not attempting to connect to auth server using 127.0.0.1 ? ( the client has special processing when it encounters attempts to connect to loopback )
"Ich was in one sumere dale,
in one suthe diyhele hale,
iherde ich holde grete tale
an hule and one niyhtingale."

broxen
Posts: 60
Joined: Tue Jun 07, 2016 8:26 am
Location: USA
Contact:

### Re: Anything I can do?

Actually I was. Do I need to run the server on a separate computer? Or can I just connect to my own IP?
-broxen

nemerle
Posts: 397
Joined: Thu Jan 10, 2013 3:40 pm

### Re: Anything I can do?

Connect to your own IP, it should be enough ( just make sure the server is configured to listen on your IP too :) )
"Ich was in one sumere dale,
in one suthe diyhele hale,
iherde ich holde grete tale
an hule and one niyhtingale."

Aiden
Posts: 3
Joined: Tue Oct 24, 2017 2:22 pm

### Re: Anything I can do?

Is this referring to the client/server commands? I am just starting to look into the binaries and trying to understand the encryption scheme. Do you already have the key exchange and packet encryption figured out? Also, is there a list of client/server commands that have been deciphered?

### Who is online

Users browsing this forum: No registered users and 3 guests