up to date with server

This commit is contained in:
chimchooree
2020-11-24 11:37:51 -06:00
parent 9a18189000
commit 2224957b7c
69 changed files with 1765 additions and 1086 deletions
@@ -0,0 +1,288 @@
<code>/******************************************************************************/</code>
<code>// SIMPLE INPUT RECORD/PLAYBACK</code>
<code>// (c) 2015 Brian Provinciano</code>
<code>//</code>
<code>// You are free to use this code for your own purposes, no strings attached.</code>
//
// This is a very basic sample to record and playback button input.
// It's most useful when activated on startup, deactivated on shutdown for
// global button recording/playback.
//
// For details on more advanced implementations, see my GDC 2015 session:
// -> Automated Testing and Instant Replays in Retro City Rampage
// The slides and full video will be available on the GDC Vault at a later date.
/******************************************************************************/
/******************************************************************************/
// wrap it so it can be conditionally compiled in.
// for example, set INPUTREPLAY_CAN_RECORD to 1 to play the game and record the input, set it to 0 when done
// INPUTREPLAY_CAN_RECORD takes priority over INPUTREPLAY_CAN_PLAYBACK
#define INPUTREPLAY_CAN_PLAYBACK 1
#define INPUTREPLAY_CAN_RECORD 1
#define INPUTREPLAY_INCLUDED (INPUTREPLAY_CAN_PLAYBACK || INPUTREPLAY_CAN_RECORD)
/******************************************************************************/
#if INPUTREPLAY_INCLUDED
#define INPUT_BUTTONS_TOTAL 32 // up to 32
#define MAX_REC_LEN 0x8000 // the buffer size for storing RLE compressed button input (x each button)
/******************************************************************************/
typedef struct
{
unsigned short *rledata;
unsigned short rlepos;
unsigned short datalen;
unsigned short currentrun;
} ButtonRec;
/******************************************************************************/
// if INPUTREPLAY_CAN_RECORD, as soon as this class is instanced, it will automatically record when instanced/created.
// statically creating this as a global will blanket the entire play session
//
// if INPUTREPLAY_CAN_PLAYBACK, playback will begin as soon as LoadFile() is used
//
class SimpleInputRec
{
unsigned int m_buttonstate;
ButtonRec m_buttons[INPUT_BUTTONS_TOTAL];
bool m_bRecording;
unsigned char* m_data;
public:
SimpleInputRec()
: m_buttonstate(0)
, m_data(NULL)
, m_bRecording(true)
{
}
~SimpleInputRec()
{
if(m_data)
{
#if INPUTREPLAY_CAN_RECORD
WriteToFile();
#endif
delete[] m_data;
}
}
// run each frame before the game uses the live button input.
// when recording, it saves the live input
// during playback, it overwrites the live input
void Update(bool bForce = false);
// to start a playback
#if INPUTREPLAY_CAN_PLAYBACK
bool LoadFile(KSTR szfilename);
#endif
// to finish recording
#if INPUTREPLAY_CAN_RECORD
void WriteToFile();
#endif
};
/******************************************************************************/
void SimpleInputRec::Update(bool bForce)
{
#if INPUTREPLAY_CAN_RECORD
if(m_bRecording)
{
unsigned int newbuttons = nesinput.buttons;
// allocate and initialize
if(!m_data)
{
m_data = new unsigned char[INPUT_BUTTONS_TOTAL * MAX_REC_LEN * 2];
unsigned short* dataptr = (unsigned short*)m_data;
for(int i=0; i<INPUT_BUTTONS_TOTAL; ++i)
{
ButtonRec& btn = m_buttons[i];
btn.rledata = dataptr;
dataptr += MAX_REC_LEN;
btn.rlepos = 0;
btn.currentrun = 0;
btn.datalen = MAX_REC_LEN;
}
}
// write RLE button bit streams
for(int i=0; i<INPUT_BUTTONS_TOTAL; ++i)
{
ButtonRec& btn = m_buttons[i];
if(bForce || (newbuttons&(1<<i)) != (m_buttonstate&(1<<i)) || btn.currentrun==0x7FFF)
{
if(btn.currentrun)
{
int bit = (m_buttonstate>>i)&1;
btn.rledata[btn.rlepos++] = (bit<<15) | btn.currentrun;
}
btn.currentrun = bForce? 0 : 1;
}
else
{
++btn.currentrun;
}
}
m_buttonstate = newbuttons;
}
#endif
#if INPUTREPLAY_CAN_PLAYBACK
if(!m_bRecording)
{
bool bIsRunning = false;
for(int i=0; i<INPUT_BUTTONS_TOTAL; ++i)
{
ButtonRec& btn = m_buttons[i];
if(btn.rledata)
{
bIsRunning = true;
if(!btn.currentrun && btn.rlepos<btn.datalen)
{
unsigned short value = btn.rledata[btn.rlepos++];
btn.currentrun = value&0x7FFF;
m_buttonstate &= ~(1<<i);
m_buttonstate |= ((value>>15)&1)<<i;
--btn.currentrun;
}
else
{
if(btn.currentrun)
{
--btn.currentrun;
}
else if(btn.rlepos==btn.datalen)
{
btn.rledata = NULL;
}
}
}
}
if(bIsRunning)
{
// TODO: this is where you can overwrite the live button state to the prerecorded one
systeminput.buttons = m_buttonstate;
}
}
#endif
}
/******************************************************************************/
#if INPUTREPLAY_CAN_PLAYBACK
bool SimpleInputRec::LoadFile(KSTR szfilename)
{
for(int i=0; i<INPUT_BUTTONS_TOTAL; ++i)
{
ButtonRec& btn = m_buttons[i];
btn.datalen = 0;
btn.rledata = NULL;
btn.rlepos = 0;
btn.currentrun = 0;
}
delete[] m_data;
m_bRecording = false;
if(fcheckexists(szfilename))
{
FILE* f = fopen(szfilename, "wb");
if(f)
{
// WARNING: You'll want to do more error checking, but just to keep it simple...
fseek(f,0,SEEK_END);
unsigned long filelen = ftell(f);
fseek(f,0,SEEK_SET);
m_data = new unsigned char[filelen];
fread(m_data, 1, filelen, f);
fclose(f);
unsigned char* bufptr = m_data;
int numbuttons = bufptr[0] | (bufptr[1]<<8);
bufptr += 2;
if(numbuttons <= INPUT_BUTTONS_TOTAL)
{
for(int i=0; i<numbuttons; ++i)
{
ButtonRec& btn = m_buttons[i];
btn.datalen = bufptr[0] | (bufptr[1]<<8);
bufptr += 2;
}
for(int i=0; i<numbuttons; ++i)
{
ButtonRec& btn = m_buttons[i];
if(btn.datalen)
{
// WARNING: Endian dependent for simplcicity
btn.rledata = (unsigned short*)bufptr;
bufptr += btn.datalen*2;
}
}
}
return true;
}
}
return false;
}
#endif
/******************************************************************************/
#if INPUTREPLAY_CAN_RECORD
void SimpleInputRec::WriteToFile()
{
if(m_data && m_bRecording)
{
Update(true);
FILE* f = fopen("_autorec.rec","wb");
if(f)
{
fputc(INPUT_BUTTONS_TOTAL, f);
fputc(0, f);
for(int i=0; i<INPUT_BUTTONS_TOTAL; ++i)
{
ButtonRec& btn = m_buttons[i];
fputc((unsigned char)btn.rlepos, f);
fputc(btn.rlepos >> 8, f);
}
for(int i=0; i<INPUT_BUTTONS_TOTAL; ++i)
{
ButtonRec& btn = m_buttons[i];
// WARNING: Endian dependent for simplcicity
fwrite(btn.rledata, 2, btn.rlepos, f);
}
fclose(f);
}
}
}
#endif
/******************************************************************************/
#endif // INPUTREPLAY_INCLUDED
</code>
@@ -0,0 +1,36 @@
<!--200918-->
<h1>my favorite GDC talks: Empathizing with Steam: How People Shop for Your Game by Chris Zukowski (2020) </h1>
september 18, 2020<br>
marketing, sale, steam, wishlist<br>
<br>
<a href="https://www.youtube.com/watch?v=UJiv14uPOac">Empathizing with Steam: How People Shop for Your Game by Chris Zukowski (2020)</a> - tips for how to design your Steam store page based on Zukowski's screenshare and shopping diary observations of ordinary people shopping on Steam
<ul>
<li>Essentially, make your gameplay genre absolutely clear within the first 4 screenshots and in the short description so that people will wishlist your game to buy during a seasonal Steam Sale.</li>
<li>Approach your wishlisters as complete newcomers. Jazz up your Steam page before a Steam Sale. Release an update, post in your forums, put a Santa hat on your character. When wishlisters return to your page, they will see an active game and be sold on it all over again.</li>
<li>His conclusions are very similar to how I shop on Steam, except I could care less for the tag section.</li>
<li>first 4 images are shown when hovering over thumbnail in Steam. Make them represent the pillars of your gameplay, so the genre is clear.</li>
<li>don't try to coerce new audiences into trying your game. try to find your audience and show them exactly what they are looking for.
<li>include UI in screenshots, so gamers can decipher the genre and some gameplay mechanics</li>
<li>Gamers naturally compare new games with the leading games of their genres to decide whether they will enjoy it.</li>
<li>indie romance authors (who compete on a market of 8 million vs. steam's 40 thousand) use clear visual language in their coverart: tartan kilt + sword + distant castle = highland romance; animal behind a hunk = shapeshifter romance; woman in front of group of hunks = reverse harem. So a reader who sees a cover with multiple guys and a wolf, she knows it's a reverse harem of shape-shifting wolf boys without any descriptions or trailers.</li>
<li>applying the wisdom of the romance authors, understand your audience's genre interests as sub-sub-sub-genres and make your genre crystal clear in your store page.</li>
<li>Apply the tropes of your genre. The FPS coverart guy with the gun on all CoD and BF games, the two Street Fighter healthbars on the top of any fighter game</li>
<li>Part 4 - How to manage a Steam sale</li>
<li>People check their wishlist during sales, looking for discounts.</li>
<li>People don't remember why they wishlisted games, so your page must look fresh even to your wishlist crew. Before a sale, post an update, theme your capsule image, post an announcement, upload a small patch, and comment in the forums.</li>
<li>People remember games that are always on sale. Frequent sales increase familiarity.</li>
<li>Power users use gg.deals or steamdb to track historic lows. Strategically staircase your way down over time. Coincide your all-time-lows with Big Steam Sales. Harness the fear of missing out.</li>
<li>How to get from wishlist to cart</li>
<li>Before clicking 'buy,' they check developer and publisher for someone they recognize or trust. They check 'more from this studio' to check familiarity with the dev's other games.</li>
<li>Release more games to build this trust and familiarity.</li>
<li>They check the 'relevant to you' bar to see if the genre matches games they've played and that friends with similar taste also like the game.</li>
<li>How to manage your sales pricing</li>
<li>How to get from cart to library</li>
<li>After loading cart, they ask their friends whether they should go for it.</li>
<li>Your current customers have to become ambassadors to complete this purchase loop. Treat them well! </li>
<li>Abandon cart recovery in other online retailers beckons shoppers back before the sale ends. Steam lacks this, potentially losing devs 10% in revenue - https://www.annexcloud.com/blog/31-shopping-cart-abandonment-statistics-for-2018/</li>
<li>Do a second marketing push during the last day of a sale.</li>
<li>5 - How to wishlisters into buyers</li>
<li></li>
</ul>
<br>