Tech guy for special events too busy with too many things things.
86 stories
·
1 follower

smarter-than-the-republicans:manhattanrf:jaylacucu:unashamedly-en...

10 Shares


smarter-than-the-republicans:

manhattanrf:

jaylacucu:

unashamedly-enthusiastic:

loveiseldritch:

papprekakinga:

Always reblog

As a former zookeeper we would hear this a lot. “If you don’t study hard you’ll end up cleaning poop for a living.” It’s the one time we’re allowed to go off on the visitors. I once heard my boss rant for five minutes at a lady, in front of her kids, about how he had a Master’s degree, how people literally worked there for free, and how dare she judge people without bothering to know anything about them. Later that day his boss came by and said, roughly, “She told us what happened. Thanks for not throwing anything this time.”

I can count on one hand the amount of times I have gone off on people, but employment snobbery gives me the rage. I was showing the new kid how to use the fry scoop at McDonald’s “.. like this, and then just sort of hold it perpendicular and give it one tap..”

And the new kid sniggered “isn’t perpendicular a bit of a big word for McDonald’s?”

Something in me was just so annoyed by this 16yr old who was learning to work right next to me and somehow felt above us? Fuck that shit. I pointed at the people just on the floor and went off, “she’s a 4th year law student, she’s the primary career for her terminally ill daughter, he raises 100,000 for charity every year, she manages 3 stores and more than £16mil in turnover a year. What the fuck do you do?”

He just sort of mumbled “I didn’t know”

“you shouldn’t have to know, you’re not better than us. So. You tap it once and then move it here to release…”

“I didn’t know.”

“You shouldn’t have to know,”

yes to all this because workers can be educated and intelligent, but also, even if workers are formally uneducated or dont know big words that doesnt mean they arent equally deserving of respect

Zookeepers bust their asses shoveling shit and feeding apex predators so you can stare at an elephant without flying to Asia or Africa.

Fast food workers bust their asses surrounded by hot ovens and boiling oil so you can get food quickly without having to make it yourself or even learn how.

Janitors bust their asses cleaning up the most vile things humans can do to a public room so you don’t have to tiptoe around human waste everywhere you go.

Mail carriers bust their asses going door to door in near-fatal heat/cold and have to deal with the possibility of getting attacked by your poorly-trained pets so you don’t have to drive to the post office every single day.

Warehouse workers bust their asses making sure YOUR latest Amazon crap doesn’t just disappear into thin air.

And retail workers bust their asses coddling and picking up after you like your parents because none of you know how to read a price tag or stop deliberately miss-shelving things you never wanted.

But sure, go ahead and act like you wouldn’t be dead in a week without these people.

Read the whole story
elwillow
187 days ago
reply
Ottawa, Ontario
popular
208 days ago
reply
Share this story
Delete

BCCLA posts CSE documents

1 Share

Yesterday, the British Columbia Civil Liberties Association (BCCLA) posted an important collection of 284 documents relating to the operations of the Communications Security Establishment. The documents provide a unique window into the ways the statutory provisions governing CSE were interpreted and operationalized by the agency in the period between 2001, when CSE's first statutory mandate was added to the National Defence Act, and the 2019 entry into force of the CSE Act. They also provide rare insight into the way CSE's signals intelligence (SIGINT) and information technology security (ITSEC) programs actually work.

In 2013, in the wake of the Snowden revelations, the BCCLA took the government to court, alleging that CSE’s bulk collection of metadata and incidental collection of private communications violated Canadians’ Charter rights to privacy. The case, which went on for several years, took place behind closed doors, and is likely ultimately to have played an important role in the government's decision to enact a number of reforms to CSE's powers and the oversight and review mechanisms for the agency in the CSE Act and other parts of Bill C-59, passed in 2019. (You can read more about the litigation here.)

During the course of the litigation, the BCCLA was provided with a large body of documents concerning CSE's operations. Although heavily redacted in many parts, these documents contained a lot of never previously revealed information about the agency's activities, with particular emphasis on the rules and procedures governing the collection and handling of communications and other information concerning persons located in Canada and Canadians located anywhere by CSE's signals intelligence (SIGINT) and information technology security (ITSEC) programs.

Unfortunately, they were provided under a confidentiality undertaking that prevented the BCCLA from making them public. However, in 2017 I made an access to information request for the documents, and eventually, following an appeal to the Information Commissioner, they were provided to me with no additional redactions. The government then released the BCCLA from its undertaking.

Now the BCCLA has made the collection, comprising over 4,900 pages of documents, available for download on its website. You can find the links at the end of Greg McMullen's guide to their contents.

I've also put together some introductory notes here.

The following key operational policy documents are included in the collection:

OPS-1, Protecting the Privacy of Canadians and Ensuring Legal Compliance in the Conduct of CSEC Activities (AGC 0022)

OPS-1-1, Operational Procedures for the Release of Suppressed Information from SIGINT Reports (AGC 0020) (28 September 2012 version) and OPS-1-1, Policy on Release of Suppressed Information (AGC 0253) (14 November 2014 version)

OPS-1-6, Operational Procedures for Naming and Releasing identities in Cyber Defence Reports (AGC 0011)

OPS-1-7, Operational Procedures for Naming in SIGINT Reports (AGC 0019)

OPS-1-8, Operational Procedures for Policy Compliance Monitoring to Ensure Legal Compliance and the Protection of the Privacy of Canadians (AGC 0024)

OPS-1-10, Operational Procedures for Metadata Analysis [redacted] (AGC 0012)

OPS-1-11, Retention Schedules for SIGINT Data (AGC 0007)

OPS-1-13, Operational Procedures Related to Canadian [redacted] Collection Activities (AGC 0023)

OPS-1-15, Operational Procedures for Cyber Defence Activities Using System Owner Data (AGC 0018)

OPS-1-16, Policy on Metadata Analysis for Foreign Intelligence Purposes (AGC 0279)

OPS-3-1, Operational Procedures for [redacted; probably "Computer Network Exploitation"] Activities (AGC 0026)

OPS-6, Policy on Mistreatment Risk Management (AGC 0266).

These twelve operational policy documents provide the most detailed window into the policies that govern CSE's operations ever made available to the public. It is important to note that all were superseded in 2018 when CSE introduced an entirely rewritten Mission Policy Suite in preparation for the passage of the CSE Act. However, it is likely that most of the details of those policies remain unchanged, so the documents also provide the best currently available insight into the likely parameters of present operational policies at the agency.

The collection also contains numerous other documents, training materials, and briefing decks that provide further insight into CSE policies and activities. These include:

- The Ministerial Directive issued by the Minister of National Defence on CSE use of metadata (both the 9 March 2005 version (AGC 0004) and the 21 November 2011 version (AGC 0017)).

- The Ministerial Directive on the Integrated SIGINT Operational Model (AGC 0076), which governs CSE's relationship with Canadian military SIGINT activities.

- Examples of the annual Ministerial Authorizations issued under the pre-2019 system to authorize CSE collection activities risking the inadvertent collection of Canadian private communications. Examples of the background memos provided to the Minister of National Defence to explain proposed Ministerial Authorizations are also in the collection.

- CSE's classified Annual Reports to the Minister of National Defence for fiscal years 2010-11, 2011-12, 2012-13, and 2013-14.

- Copies of many of the memoranda of understanding between CSE and client departments on the provision of SIGINT services.

- Subsidiary policy and procedure documents on a wide range of subjects, such as Producing Gists for Indications and Warning Purposes (AGC 0134), Targeting Identifiers for [Foreign Intelligence] under Mandate A (AGC 0135), and Foreign Assessments and Protected Entities (AGC 0136).

- Two training manuals for CSE employees: SIGINT 101 Orientation Program (AGC 0182), an introduction to CSE's SIGINT program, and DGI [Director General Intelligence] Familiarization Manual (AGC 0193), an introduction to work as a SIGINT analyst at CSE.

- Numerous classified reports from CSE's pre-2019 watchdog body, the Office of the Communications Security Establishment Commissioner (OCSEC), and CSE's responses to those reports. These include OCSEC's 2015 review of CSE's metadata activities (AGC 0278), which examines a series of failures by CSE to protect information about Canadians in metadata shared with foreign partners. This report is the best source of information available on those events, which led to the only declaration that CSE had failed to comply with the law that OCSEC ever issued.

In addition to broader policy questions, the documents are an unparalleled source of background information about aspects of CSE's activities. For example, one OCSEC review (AGC 0110) describes the nature of the Client Relations Officer (CRO) system that CSE uses to deliver SIGINT products to many of its government clients. Another (AGC 0179) contains the first data ever released to the public on the percentage of requests made by SIGINT clients for Canadian Identity Information that were approved by CSE (1113 of 1119, or more than 99%). In 2021, the National Security and Intelligence Review Agency (NSIRA), which replaced OCSEC in 2019, was able to release additional data on CSE's approval rate for requests, possibly in part because the BCCLA release had already established that such data could be declassified.

In other cases the documents provide insight into aspects of CSE's activities that the agency is still redacting from NSIRA reports. For example, pages 19-21 of this NSIRA report released in 2021 discussed a flawed policy related to privacy protection that was later rescinded by CSE, but NSIRA was evidently unable to include any information about the nature of the policy in its report. The key details of the policy in question can be found on pages 30-31 of OPS-1-7, Operational Procedures for Naming in SIGINT Reports (AGC 0019).

In other cases, one can observe the evolution of CSE policies over time. For example, in document AGC 0182 (p. 99) it is explained that "we [CSE] do not have to protect the privacy of non-Canadians in Canada. This means that in reports we can name people who are in Canada and who fall into certain categories like holding work or student visas, or who are illegal immigrants." But document AGC 0206 (p. 122) reports that this policy was changed in April 2014, with CSE's privacy policies now covering all persons in Canada. (Given the timing of this change, it's likely that it was made in response to the BCCLA's legal action.)

The documents are also a gold mine of information on the official definitions of key terms used by CSE, encompassing concepts such as Canadian Privacy-Related Information, Metadata, and Contact Chaining. The BCCLA has put together a guide to many of those terms here (but note that their glossary is "a work in progress and not intended as a formal dictionary").

Some of the documents in the BCCLA collection have previously been released to individual requesters through the Access to Information Act. But in many cases the versions released were significantly more heavily redacted than the versions provided to the BCCLA. (The parts of the documents pertaining to CSE's mandate to provide support to federal law enforcement and security agencies are an exception, however, as those parts were redacted in their entirety from the BCCLA documents as "not relevant" to their case.) In addition, in many cases documents released to individual requesters are never published or otherwise made accessible to other researchers or the general public. 

The BCCLA collection is unique in providing systematic access to these documents for online research and downloading.

Enjoy!

 

Read the whole story
elwillow
195 days ago
reply
Ottawa, Ontario
Share this story
Delete

Saturday Morning Breakfast Cereal - Drugs

4 Shares


Click here to go see the bonus panel!

Hovertext:
It's not a scam if it's just 50 trillion micro-scams!


Today's News:
Read the whole story
elwillow
254 days ago
reply
Ottawa, Ontario
Share this story
Delete

Passkeys as a tool for user retention

1 Share
Passkeys as a tool for user retention

With the release of iOS 16 and MacOS Ventura, we are now in the age of passkeys. This is happening through WebAuthn, a specification written by the W3C and FIDO with the involvement of all of the major vendors such as Google, Mozilla, etc. The basic premise is familiar to anyone who has used SSH in their career: you login through the distribution of public keys, keeping the private key on the device.

Like all security initiatives, someone took the problem of "users sometimes reuse passwords" and decided the correct solution is to involve biometric security when I log into my coffee shop website to put my order in. I disagree with the basic premise of the standard on a personal level. I think it probably would have been fine to implement an API where the service simply requested a random password and then stored it in the existing browser password sync. It would have solved the first problem of password reuse, allowed for export and didn't require a total change in how all authentication works. However I am not the kind of person who writes these whitepapers full of math so I defer to the experts.

How WebAuthn works is the server receives from the user a public key and randomly generated ID. This private key is distributed and stored in the client vendor sync process, meaning it is available to different devices as long as those devices exist within the same vendor ecosystem. This stuck out to me as a fascinating transition for passwords and one with long-term implications for user retention across the different platforms.

Imagine being Apple or Google and getting to tell users "if you abandon this platform, you are going to lose every login you have". This is a tremendous threat and since all platforms will effectively be able to share it at the same time, not a legal threat. Let's get into the details of what WebAuthn is and how it works, then walk through how this provides tremendous value to platform holders as a way of locking in users.

WebAuthn Properties

WebAuthn is a web-based API that allows web servers, called Relying Parties to communicate with authenticators on a users device. I'll refer to these as RPs from now on. To get started, the wserver creates new credentials by calling navigator.credentials.create() on the client.

const credential = await navigator.credentials.create({
    publicKey: publicKeyCredentialCreationOptions
});

This object has a number of properties.

  • challenge: a buffer of random bytes generated by the server to prevent replay attacks.
  • rp: basically the website, needs to be a subset of the domain currently in the browser.
  • user: information about the user. Suggested not to use PII data but even if you use name and displayName it doesn't appear that this is ever relayed to the rp source
  • pubKeyCredParams: what public keys are acceptable to the server
  • authenticatorSelection: do you want anything to be allowed to be an authenticator or do you want a cross-platform authenticator like only a YubiKey
  • timeout: self-documenting
  • attestation: information from the authenticator that could be used to track users.

Attestation

What you are getting back as the service is the attestation statement, which is a somewhat vague concept. It's a signed data object that includes info about the public key and some other pieces of information. You can see the generic object below

Passkeys as a tool for user retention
Source

This part is kind of interesting. There are actually 4 tiers of "information you get back about the user".

  • none: You don't want anything and is the default
  • indirect: the client is allowed how to obtain such a statement. The client may replace an authenticator-generated statement with one generated with an Anonymization CA.
  • direct: you want the statement
  • enterprise: you want the statement that may include uniquely identifying information. The authenticator needs to be configured to say "this rp ID is allowed to request this information", so presumably this will be for devices enrolled in some sort of MDM.

You get back an attestationObject at the end of all of this which basically allows you to parse metadata about the registration event as well as the public key, along with the fmt of the attestation which is effectively the type of authenticator you used. Finally you have the statement itself.

When a user wants to sign in, the process works pretty simply. We call navigator.credentials.get() on the client, which basically says go get me the credentials I specify in allowCredentials. You can say what the ID is and how to get the credentials (through usb, bluetooth, nfc, etc).

For further code samples and a great walkthrough I recommend: https://webauthn.guide/

The key to how this all works is that the private key is synced by the vendor to the different devices (allowing for portability) but also allows for phone delegation. So for instance if you are a Windows Chrome user and want to sign in using passkeys, you can, as long as you still have the Apple device that you originally created the passkey on.

Passkeys as a tool for user retention
Good diagram if you are using the Okta service, but still valuable either way

Passkey cross-device flow

  1. The vendor supplies a "Passkey from nearby devices" option when the user is logging in
  2. The web site displays a QR code
  3. The device which contains the passkey points its camera and starts an auth flow
  4. The two devices perform a Bluetooth handshake to ensure they are actually near each others and agree on a server to use as an exchange post
  5. The device with the passkey is used to perform the actual auth process.
  6. Now in theory the service should offer some sort of "would you like to make a new passkey on this device".

At no point did you transfer the private key anywhere. That entire sync process is controlled by the vendor, meaning your option for portable authentication is going to be a roaming authentiator (aka a YubiKey).

It's important to note here that a lot of assumptions have been made about developers around the requirement of local storage of private keys. This isn't necessarily the case. Authenticators have the option of not storing their private keys locally at all. You have the option of instead storing the private keys with the rp, encrypted under a symmetric key held by the authenticator. This elasticity to the spec comes up a lot, with many decisions deferred to the vendors.

Why Does This Matter?

WebAuthn has taken a strong "decentralized" design philosophy, which makes sense until you realize that the inability to export private keys isn't....really true. Basically by submitting an attestation, the vendor is saying "these devices private keys cannot be stolen". You can see the initial conversation on GitHub here. It's making the problem someone else's issue.

By saying, in essence, portability of private keys is not a concern of the spec and leaving it entirely in the hands of vendors, we have created one of the greatest opportunities for user lock-in in recent history. It is now on the individual services and the vendors to allow for users to seamlessly sign in using different devices. The degree by which platform owners want to allow these devices to work with each other is entirely within their own control.

We can see that vendors understand this to some extent. Apple has announced that once passkey is supported in the OS, device-bound keys will no longer be supported. You will not have the option of not using iCloud (or Chrome Sync services). Administrators will likely not love the idea of critical keys being synced to devices possibly beyond their control (although the enterprise attestation provides some protection against this). So already in these early days we see a lot of work being done to ensure a good user experience but at the cost of increased vendor buy-in.

Scenario

You are a non-technical user, who used their iPhone in the normal way. When presented with a login you let the default of "use passkeys" ride, without doing anything special. You lose your phone, but don't own any other Apple products. By default iCloud Keychain only works on iOS, iPadOS and macOS. In order to seamlessly log into any service that you registered through your phone with passkeys, you have to purchase another Apple product.

If you attempt to switch to Android, while it supports passkeys, it is between you and the RP on how controlling access to your original account will work. Since allowing users to reset their passkeys through requesting a passkey reset through an email link eliminates a lot of the value of said service, I'm not exactly sure how this is going to be handled. Presumably there will need to be some other out-of-band login.

Also remember that from the RPs side, what you are getting is almost no information about the user. You aren't even (confidently) getting what kind of authenticator. This is great from a GDPR perspective, not having to hold email addresses and names and all sorts of other PII in your database (and does greatly eliminate many of the security concerns around databases). However if I am a web developer who goes "all-in" with this platform, it's hard to see how at some point I'm not going to fall back to "email this user a link to perform some action" and require the email address for account registration.

On the RP side they'll need to: verify this is the right user (hopefully they got some other ID in the registration flow), remove the passphrase and then have the user enroll again. This isn't a terrible flow, but is quite a bit more complicated than the story today of: log in again through SSO or by resetting a password by sending a reset link to email.

What about Chrome?

Chrome supports the standard, but not in a cross-platform way.

Passkeys are synchronized across devices that are part of the same ecosystem. For example, if a user creates a passkey on Android, it is available on all Android devices as long as the user is signed in to the same Google account. However, the same passkey is not available on iOS, macOS or Windows, even if you are using the same browser, like Chrome.
Source

Ultimately it is the hardware you are generating the key on that matters for vendor syncing, not the browser or OS.

The result of this is going to be pretty basic: as years go on, the inertia required to switch platforms will increase as more services are added as passkeys. There exists no way currently that I'm able to find that would allow you to either: add a secondary device to the exchange process or to bulk transfer out of Vendor A and into Vendor B. Instead any user who wants to switch services will need to go through the process of re-enrolling in each services with a new user ID, presumably hoping that email was captured as part of the sign-up flow so that the website or app can tie the two values together.

There is a proposed spec that would allow for dual enrollment, but only from the start. Meaning you would need to have your iOS authenticator, then attach your Chromebook authenticator to it from the start. There is no way to go back through and re-sync all logins with the new device and you would need constant access to both devices to complete the gesture element of the auth.

Yubico proposal

You can read that proposal here.

Yubico has an interesting idea here based on ARKG or Asynchronous Remote Key Generation. The basic idea is that you have a primary authenticator and a secondary authenticator that has no data transfer between the two. The proposed flow looks as follows

  • Backup device generators a private-public key pair and transfers the public key to the primary authenticator
  • This is used by the primary authenticator to derive new public keys on behalf of the backup device
  • Then the primary generates a new pair for each backup device registered and sends this on to the RP along with its primary key.
  • If the primary disappears, the backup device can request the cred from the RP and use it to derive the key used. In order to retrieve the cred associated with a user, there needs to be some sort of identifier outside of the user ID in the spec which is a random value not surfaced to the user.

For more math information on the spec itself check this paper out.

ARKG functionality. ARKG allows arbitrary public keys pk′ to
be derived from an original pk, with corresponding sk′ being cal-
culated at a later time—requiring private key sk for the key pair
(sk, pk) and credential cred.
Definition 3.1 (ARKG). The remote key generation and recovery
scheme ARKG B (Setup, KGen, DerivePK, DeriveSK, Check) con-
sists of the following algorithms:
• Setup(1𝜆 ) generates and outputs public parameters pp =
((G, 𝑔, 𝑞), MAC, KDF1, KDF2) of the scheme for the security
parameter 𝜆 ∈ N.
• KGen(pp), on input pp, computes and returns a private-
public key pair (sk, pk).
• DerivePK(pp, pk, aux) probabilistically returns a new public
key pk′ together with the link cred between pk and pk′, for
the inputs pp, pk and auxiliary data aux. The input aux is
always required but may be empty.
• DeriveSK(pp, sk, cred), computes and outputs either the new
private key sk′, corresponding to the public key pk′ using
cred, or ⊥ on error.
• Check(pp, sk′, pk′), on input (sk′, pk′), returns 1 if (sk′, pk′)
forms a valid private-public key pair, where sk′ is the cor-
responding private key to public key pk′, otherwise 0.
Correctness. An ARKG scheme is correct if, ∀𝜆 ∈ N, pp ←
Setup(1𝜆 ), the probability Pr [Check(pp, sk′, pk′) = 1] = 1 if
(sk, pk) ← KGen(pp);
(pk′, cred) ← DerivePK(pp, pk, ·);
sk′ ← DeriveSK(pp, sk, cred).
Look at all those fun characters.

Challenges

The WebAuthn presents a massive leap forward for security. There's no disputing that. Not only does it greatly reduce the amount of personal information flowing around the auth flow, it also breaks the reliance on email address or phone numbers as sources of truth. The back-bone of the protocol is a well-understand handshake process used for years and extremely well-vetted.

However the spec still has a lot of usability challenges that need to be addressed especially as adoption speeds up.

Here are the ones I see in no particular order:

  • Users and administrators will need to understand and accept that credentials are backed up and synced across unknown devices employing varying levels of biometric security.
  • Core to the concept of WebAuthn is the idea of unlinkability. That means different keys must be used for every new account at the RP. Transferring or combining accounts is a problem for the RP which will require some planning on the part of service providers.
  • In order to use this feature, services like iCloud Sync will be turned on and the security of that vendor account is now the primary security of the entire collection of passwords. This is great for consumers, less great for other systems.
  • There currently exists no concept of delegation. Imagine I need to provide you with some subset of access which I can delegate, grant and revoke permissions, etc. There is an interesting paper on the idea of delegation which you can find here.
  • Consumers should be aware of the level of platform buy-in they're committing to. Users acclimated to Chromebooks and Chrome on Windows being mostly interchangeable should be made aware that this login is now tied to a class of hardware.
  • We need some sort of "vendor exchange" process. This will be a challenge since part of the spec is that you are including information about the authenticator (if the RP asks for it). So there's no reason to think a service which generated an account for you based on one type of authenticator will accept another one. Presumably since the default is no information on this, a sync would mostly work across different vendors.
  • The vendor sync needs to extend outside of OEMs. If I use iCloud passkeys for years and then enroll in 1Password, there's no reason why I shouldn't be able to  move everything to that platform. I understand not allowing them to be exposed to users (although I have some platform ownership questions there like 'isn't it my passkey'), but some sort of certified exchange is a requirement and one that should have been taken care of before the standard was launched.

Conclusion

This is a giant leap forward in security for average users. It is also, as currently implemented, one of the most effective platform lock-ins I've ever seen. Forget the "green text" vs "blue text", as years go on and users rely more and more on passkeys for logins (which they should), switching platforms entirely will go from "a few days of work" to potentially needing to reach out and attempt to undo every single one of these logins and re-enroll. For folks who keep their original devices or a device in the ecosystem, this is mostly time consuming.

For users who don't, which will be a non-trivial percentage (why would a non-technical user keep their iphone around and not sell it if they have a brand new android), this is going to be an immense time commitment. This all assumes a high degree of usage of this standard, but I have trouble imagining web developers won't want to use this. It is simple a better more secure system that shifts a lot of the security burden off of them.

Read the whole story
elwillow
319 days ago
reply
Ottawa, Ontario
Share this story
Delete

CodeSOD: Swwwitch

1 Share

We're going to do something a little different. I don't like to do posts about game related code. Games are entirely about shipping something out the door on tight timelines and tight budgets, and it's very much the category of "if it works it's good". There are exceptions, like when you ship an actual WTF, but "bad game code" is not really that interesting.

Awhile back, indie game VVVVVV went open source which gives us a picture of how the sausage is actually made. Now, this is emphatically not a WTF, this isn't wrong or a mistake, this is just the kind of thing that gets a game shipped, especially when it's a small budget indie game, by basically one person.

So instead of getting angry or annoyed, let's just marvel at this 3,440 line switch which represents the game's main loop.

switch(state) { case 0: //Do nothing here! Standard game state break; case 1: //Game initilisation state = 0; break; case 2: //Opening cutscene advancetext = true; hascontrol = false; state = 3; dwgfx.createtextbox("To do: write quick", 50, 80, 164, 164, 255); dwgfx.addline("intro to story!"); //Oh no! what happen to rest of crew etc crash into dimension break; case 4: //End of opening cutscene for now dwgfx.createtextbox(" Press arrow keys or WASD to move ", -1, 195, 174, 174, 174); dwgfx.textboxtimer(60); state = 0; break; case 5: //Demo over advancetext = true; hascontrol = false; /*dwgfx.createtextbox(" Prototype Complete ", 50, 80, 164, 164, 255); dwgfx.addline("Congrats! More Info Soon!"); dwgfx.textboxcenter(); */ startscript = true; newscript="returntohub"; obj.removetrigger(5); state = 6; break; case 7: //End of opening cutscene for now dwgfx.textboxremove(); hascontrol = true; advancetext = false; state = 0; break; case 8: //Enter dialogue obj.removetrigger(8); if (obj.flags[13] == 0) { obj.changeflag(13, 1); dwgfx.createtextbox(" Press ENTER to view map ", -1, 155, 174, 174, 174); dwgfx.addline(" and quicksave"); dwgfx.textboxtimer(60); } state = 0; break; case 9: //Start SWN Minigame Mode B obj.removetrigger(9); swnmode = true; swngame = 6; swndelay = 150; swntimer = 60 * 30; //set the checkpoint in the middle of the screen savepoint = 0; savex = 148; savey = 100; savegc = 0; saverx = roomx; savery = roomy; savedir = 0; state = 0; break; case 10: //Start SWN Minigame Mode A obj.removetrigger(10); swnmode = true; swngame = 4; swndelay = 150; swntimer = 60 * 30; //set the checkpoint in the middle of the screen savepoint = 0; savex = 148; savey = 100; savegc = 0; saverx = roomx; savery = roomy; savedir = 0; state = 0; break; case 11: //Intermission 1 instructional textbox, depends on last saved dwgfx.textboxremovefast(); dwgfx.createtextbox(" When you're NOT standing on ", -1, 3, 174, 174, 174); if (dwgfx.flipmode) { if (lastsaved == 2) { dwgfx.addline(" the ceiling, Vitellary will"); } else if (lastsaved == 3) { dwgfx.addline(" the ceiling, Vermilion will"); } else if (lastsaved == 4) { dwgfx.addline(" the ceiling, Verdigris will"); } else if (lastsaved == 5) { dwgfx.addline(" the ceiling, Victoria will"); } } else { if (lastsaved == 2) { dwgfx.addline(" the floor, Vitellary will"); } else if (lastsaved == 3) { dwgfx.addline(" the floor, Vermilion will"); } else if (lastsaved == 4) { dwgfx.addline(" the floor, Verdigris will"); } else if (lastsaved == 5) { dwgfx.addline(" the floor, Victoria will"); } } dwgfx.addline(" stop and wait for you."); dwgfx.textboxtimer(180); state = 0; break; case 12: //Intermission 1 instructional textbox, depends on last saved obj.removetrigger(12); if (obj.flags[61] == 0) { obj.changeflag(61, 1); dwgfx.textboxremovefast(); dwgfx.createtextbox(" You can't continue to the next ", -1, 8, 174, 174, 174); if (lastsaved == 5) { dwgfx.addline(" room until she is safely across. "); } else { dwgfx.addline(" room until he is safely across. "); } dwgfx.textboxtimer(120); } state = 0; break; case 13: //textbox removal obj.removetrigger(13); dwgfx.textboxremovefast(); state = 0; break; case 14: //Intermission 1 instructional textbox, depends on last saved if (dwgfx.flipmode) { dwgfx.createtextbox(" When you're standing on the ceiling, ", -1, 3, 174, 174, 174); } else { dwgfx.createtextbox(" When you're standing on the floor, ", -1, 3, 174, 174, 174); } if (lastsaved == 2) { dwgfx.addline(" Vitellary will try to walk to you. "); } else if (lastsaved == 3) { dwgfx.addline(" Vermilion will try to walk to you. "); } else if (lastsaved == 4) { dwgfx.addline(" Verdigris will try to walk to you. "); } else if (lastsaved == 5) { dwgfx.addline(" Victoria will try to walk to you. "); } dwgfx.textboxtimer(280); state = 0; break; case 15: //leaving the naughty corner obj.entities[obj.getplayer()].tile = 0; state = 0; break; case 16: //entering the naughty corner if(obj.entities[obj.getplayer()].tile == 0) { obj.entities[obj.getplayer()].tile = 144; music.playef(2, 10); } state = 0; break; case 17: //Arrow key tutorial obj.removetrigger(17); dwgfx.createtextbox(" If you prefer, you can press UP or ", -1, 195, 174, 174, 174); dwgfx.addline(" DOWN instead of ACTION to flip."); dwgfx.textboxtimer(100); state = 0; break; case 20: if (obj.flags[1] == 0) { obj.changeflag(1, 1); state = 0; dwgfx.textboxremove(); } obj.removetrigger(20); break; case 21: if (obj.flags[2] == 0) { obj.changeflag(2, 1); state = 0; dwgfx.textboxremove(); } obj.removetrigger(21); break; case 22: if (obj.flags[3] == 0) { dwgfx.textboxremovefast(); obj.changeflag(3, 1); state = 0; dwgfx.createtextbox(" Press ACTION to flip ", -1, 25, 174, 174, 174); dwgfx.textboxtimer(60); } obj.removetrigger(22); break; case 30: //Generic "run script" if (obj.flags[4] == 0) { obj.changeflag(4, 1); startscript = true; newscript="firststeps"; state = 0; } obj.removetrigger(30); state = 0; break; case 31: //state = 55; statedelay = 50; state = 0; statedelay = 0; if (obj.flags[6] == 0) { obj.changeflag(6, 1); obj.changeflag(5, 1); startscript = true; newscript="communicationstation"; state = 0; statedelay = 0; } obj.removetrigger(31); break; case 32: //Generic "run script" if (obj.flags[7] == 0) { obj.changeflag(7, 1); startscript = true; newscript="teleporterback"; state = 0; } obj.removetrigger(32); state = 0; break; case 33: //Generic "run script" if (obj.flags[9] == 0) { obj.changeflag(9, 1); startscript = true; newscript="rescueblue"; state = 0; } obj.removetrigger(33); state = 0; break; case 34: //Generic "run script" if (obj.flags[10] == 0) { obj.changeflag(10, 1); startscript = true; newscript="rescueyellow"; state = 0; } obj.removetrigger(34); state = 0; break; case 35: //Generic "run script" if (obj.flags[11] == 0) { obj.changeflag(11, 1); startscript = true; newscript="rescuegreen"; state = 0; } obj.removetrigger(35); state = 0; break; case 36: //Generic "run script" if (obj.flags[8] == 0) { obj.changeflag(8, 1); startscript = true; newscript="rescuered"; state = 0; } obj.removetrigger(36); state = 0; break; case 37: //Generic "run script" if (companion == 0) { startscript = true; newscript="int2_yellow"; state = 0; } obj.removetrigger(37); state = 0; break; case 38: //Generic "run script" if (companion == 0) { startscript = true; newscript="int2_red"; state = 0; } obj.removetrigger(38); state = 0; break; case 39: //Generic "run script" if (companion == 0) { startscript = true; newscript="int2_green"; state = 0; } obj.removetrigger(39); state = 0; break; case 40: //Generic "run script" if (companion == 0) { startscript = true; newscript="int2_blue"; state = 0; } obj.removetrigger(40); state = 0; break; case 41: //Generic "run script" if (obj.flags[60] == 0) { obj.changeflag(60, 1); startscript = true; if (lastsaved == 2) { newscript = "int1yellow_2"; } else if (lastsaved == 3) { newscript = "int1red_2"; } else if (lastsaved == 4) { newscript = "int1green_2"; } else if (lastsaved == 5) { newscript = "int1blue_2"; } state = 0; } obj.removetrigger(41); state = 0; break; case 42: //Generic "run script" if (obj.flags[62] == 0) { obj.changeflag(62, 1); startscript = true; if (lastsaved == 2) { newscript = "int1yellow_3"; } else if (lastsaved == 3) { newscript = "int1red_3"; } else if (lastsaved == 4) { newscript = "int1green_3"; } else if (lastsaved == 5) { newscript = "int1blue_3"; } state = 0; } obj.removetrigger(42); state = 0; break; case 43: //Generic "run script" if (obj.flags[63] == 0) { obj.changeflag(63, 1); startscript = true; if (lastsaved == 2) { newscript = "int1yellow_4"; } else if (lastsaved == 3) { newscript = "int1red_4"; } else if (lastsaved == 4) { newscript = "int1green_4"; } else if (lastsaved == 5) { newscript = "int1blue_4"; } state = 0; } obj.removetrigger(43); state = 0; break; case 44: //Generic "run script" if (obj.flags[64] == 0) { obj.changeflag(64, 1); startscript = true; if (lastsaved == 2) { newscript = "int1yellow_5"; } else if (lastsaved == 3) { newscript = "int1red_5"; } else if (lastsaved == 4) { newscript = "int1green_5"; } else if (lastsaved == 5) { newscript = "int1blue_5"; } state = 0; } obj.removetrigger(44); state = 0; break; case 45: //Generic "run script" if (obj.flags[65] == 0) { obj.changeflag(65, 1); startscript = true; if (lastsaved == 2) { newscript = "int1yellow_6"; } else if (lastsaved == 3) { newscript = "int1red_6"; } else if (lastsaved == 4) { newscript = "int1green_6"; } else if (lastsaved == 5) { newscript = "int1blue_6"; } state = 0; } obj.removetrigger(45); state = 0; break; case 46: //Generic "run script" if (obj.flags[66] == 0) { obj.changeflag(66, 1); startscript = true; if (lastsaved == 2) { newscript = "int1yellow_7"; } else if (lastsaved == 3) { newscript = "int1red_7"; } else if (lastsaved == 4) { newscript = "int1green_7"; } else if (lastsaved == 5) { newscript = "int1blue_7"; } state = 0; } obj.removetrigger(46); state = 0; break; case 47: //Generic "run script" if (obj.flags[69] == 0) { obj.changeflag(69, 1); startscript = true; newscript="trenchwarfare"; state = 0; } obj.removetrigger(47); state = 0; break; case 48: //Generic "run script" if (obj.flags[70] == 0) { obj.changeflag(70, 1); startscript = true; newscript="trinketcollector"; state = 0; } obj.removetrigger(48); state = 0; break; case 49: //Start final level music if (obj.flags[71] == 0) { obj.changeflag(71, 1); music.niceplay(15); //Final level remix state = 0; } obj.removetrigger(49); state = 0; break; case 50: music.playef(15, 10); dwgfx.createtextbox("Help! Can anyone hear", 35, 15, 255, 134, 255); dwgfx.addline("this message?"); dwgfx.textboxtimer(60); state++; statedelay = 100; break; case 51: music.playef(15, 10); dwgfx.createtextbox("Verdigris? Are you out", 30, 12, 255, 134, 255); dwgfx.addline("there? Are you ok?"); dwgfx.textboxtimer(60); state++; statedelay = 100; break; case 52: music.playef(15, 10); dwgfx.createtextbox("Please help us! We've crashed", 5, 22, 255, 134, 255); dwgfx.addline("and need assistance!"); dwgfx.textboxtimer(60); state++; statedelay = 100; break; case 53: music.playef(15, 10); dwgfx.createtextbox("Hello? Anyone out there?", 40, 15, 255, 134, 255); dwgfx.textboxtimer(60); state++; statedelay = 100; break; case 54: music.playef(15, 10); dwgfx.createtextbox("This is Doctor Violet from the", 5, 8, 255, 134, 255); dwgfx.addline("D.S.S. Souleye! Please respond!"); dwgfx.textboxtimer(60); state++; statedelay = 100; break; case 55: music.playef(15, 10); dwgfx.createtextbox("Please... Anyone...", 45, 14, 255, 134, 255); dwgfx.textboxtimer(60); state++; statedelay = 100; break; case 56: music.playef(15, 10); dwgfx.createtextbox("Please be alright, everyone...", 25, 18, 255, 134, 255); dwgfx.textboxtimer(60); state=50; statedelay = 100; break; case 80: //Used to return to menu from the game if(dwgfx.fademode == 1) state++; break; case 81: gamestate = 1; dwgfx.fademode = 4; music.play(6); dwgfx.backgrounddrawn = false; map.tdrawback = true; dwgfx.flipmode = false; createmenu("mainmenu"); state = 0; break; case 82: //Time Trial Complete! obj.removetrigger(82); hascontrol = false; timetrialresulttime = seconds + (minutes * 60); timetrialrank = 0; if (timetrialresulttime <= timetrialpar) timetrialrank++; if (trinkets >= timetrialshinytarget) timetrialrank++; if (deathcounts == 0) timetrialrank++; if (timetrialresulttime < besttimes[timetriallevel] || besttimes[timetriallevel]==-1) { besttimes[timetriallevel] = timetrialresulttime; } if (trinkets > besttrinkets[timetriallevel] || besttrinkets[timetriallevel]==-1) { besttrinkets[timetriallevel] = trinkets; } if (deathcounts < bestlives[timetriallevel] || bestlives[timetriallevel]==-1) { bestlives[timetriallevel] = deathcounts; } if (timetrialrank > bestrank[timetriallevel] || bestrank[timetriallevel]==-1) { bestrank[timetriallevel] = timetrialrank; if(timetrialrank>=3){ if(timetriallevel==0) NETWORK_unlockAchievement("vvvvvvtimetrial_station1_fixed"); if(timetriallevel==1) NETWORK_unlockAchievement("vvvvvvtimetrial_lab_fixed"); if(timetriallevel==2) NETWORK_unlockAchievement("vvvvvvtimetrial_tower_fixed"); if(timetriallevel==3) NETWORK_unlockAchievement("vvvvvvtimetrial_station2_fixed"); if(timetriallevel==4) NETWORK_unlockAchievement("vvvvvvtimetrial_warp_fixed"); if(timetriallevel==5) NETWORK_unlockAchievement("vvvvvvtimetrial_final_fixed"); } } savestats(map, dwgfx); dwgfx.fademode = 2; music.fadeout(); state++; break; case 83: frames--; if(dwgfx.fademode == 1) state++; break; case 84: dwgfx.flipmode = false; gamestate = 1; dwgfx.fademode = 4; dwgfx.backgrounddrawn = true; map.tdrawback = true; createmenu("timetrialcomplete"); state = 0; break; case 85: //Cutscene skip version of final level change obj.removetrigger(85); //Init final stretch state++; music.playef(9, 10); music.play(2); obj.flags[72] = 1; screenshake = 10; flashlight = 5; map.finalstretch = true; map.warpx = false; map.warpy = false; map.background = 6; map.final_colormode = true; map.final_colorframe = 1; state = 0; break; //From 90-100 are run scripts for the eurogamer expo only, remove later case 90: //Generic "run script" startscript = true; newscript="startexpolevel_station1"; obj.removetrigger(90); state = 0; break; case 91: //Generic "run script" startscript = true; newscript="startexpolevel_lab"; obj.removetrigger(91); state = 0; break; case 92: //Generic "run script" startscript = true; newscript="startexpolevel_warp"; obj.removetrigger(92); state = 0; break; case 93: //Generic "run script" startscript = true; newscript="startexpolevel_tower"; obj.removetrigger(93); state = 0; break; case 94: //Generic "run script" startscript = true; newscript="startexpolevel_station2"; obj.removetrigger(94); state = 0; break; case 95: //Generic "run script" startscript = true; newscript="startexpolevel_final"; obj.removetrigger(95); state = 0; break; case 96: //Used to return to gravitron to game if(dwgfx.fademode == 1) state++; break; case 97: gamestate = 0; dwgfx.fademode = 4; startscript = true; newscript="returntolab"; state = 0; break; case 100: // // Meeting crewmate in the warpzone // obj.removetrigger(100); if (obj.flags[4] == 0) { obj.changeflag(4, 1); state++; } break; case 101: { i = obj.getplayer(); hascontrol = false; if (obj.entities[i].onroof > 0 && gravitycontrol == 1) { gravitycontrol = 0; music.playef(1, 10); } if (obj.entities[i].onground > 0) { state++; } } break; case 102: { companion = 6; i = obj.getcompanion(6); obj.entities[i].tile = 0; obj.entities[i].state = 1; advancetext = true; hascontrol = false; dwgfx.createtextbox("Captain! I've been so worried!", 60, 90, 164, 255, 164); state++; music.playef(12, 10); } break; case 104: dwgfx.createtextbox("I'm glad you're ok!", 135, 152, 164, 164, 255); state++; music.playef(11, 10); dwgfx.textboxactive(); break; case 106: { dwgfx.createtextbox("I've been trying to find a", 74, 70, 164, 255, 164); dwgfx.addline("way out, but I keep going"); dwgfx.addline("around in circles..."); state++; music.playef(2, 10); dwgfx.textboxactive(); i = obj.getcompanion(6); obj.entities[i].tile = 54; obj.entities[i].state = 0; } break; case 108: dwgfx.createtextbox("Don't worry! I have a", 125, 152, 164, 164, 255); dwgfx.addline("teleporter key!"); state++; music.playef(11, 10); dwgfx.textboxactive(); break; case 110: { i = obj.getcompanion(6); obj.entities[i].tile = 0; obj.entities[i].state = 1; dwgfx.createtextbox("Follow me!", 185, 154, 164, 164, 255); state++; music.playef(11, 10); dwgfx.textboxactive(); } break; case 112: dwgfx.textboxremove(); hascontrol = true; advancetext = false; state = 0; break; case 115: // // Test script for space station, totally delete me! // { i = obj.getplayer(); hascontrol = false; state++; } break; case 116: advancetext = true; hascontrol = false; dwgfx.createtextbox("Sorry Eurogamers! Teleporting around", 60 - 20, 200, 255, 64, 64); dwgfx.addline("the map doesn't work in this version!"); dwgfx.textboxcenterx(); state++; break; case 118: dwgfx.textboxremove(); hascontrol = true; advancetext = false; state = 0; break; case 120: // // Meeting crewmate in the space station // obj.removetrigger(120); if (obj.flags[5] == 0) { obj.changeflag(5, 1); state++; } break; case 121: { i = obj.getplayer(); hascontrol = false; if (obj.entities[i].onground > 0 && gravitycontrol == 0) { gravitycontrol = 1; music.playef(1, 10); } if (obj.entities[i].onroof > 0) { state++; } } break; case 122: companion = 7; i = obj.getcompanion(7); obj.entities[i].tile = 6; obj.entities[i].state = 1; advancetext = true; hascontrol = false; dwgfx.createtextbox("Captain! You're ok!", 60-10, 90-40, 255, 255, 134); state++; music.playef(14, 10); break; case 124: dwgfx.createtextbox("I've found a teleporter, but", 60-20, 90 - 40, 255, 255, 134); dwgfx.addline("I can't get it to go anywhere..."); state++; music.playef(2, 10); dwgfx.textboxactive(); i = obj.getcompanion(7); //obj.entities[i].tile = 66; obj.entities[i].state = 0; break; case 126: dwgfx.createtextbox("I can help with that!", 125, 152-40, 164, 164, 255); state++; music.playef(11, 10); dwgfx.textboxactive(); break; case 128: dwgfx.createtextbox("I have the teleporter", 130, 152-35, 164, 164, 255); dwgfx.addline("codex for our ship!"); state++; music.playef(11, 10); dwgfx.textboxactive(); break; case 130: dwgfx.createtextbox("Yey! Let's go home!", 60-30, 90-35, 255, 255, 134); state++; music.playef(14, 10); dwgfx.textboxactive(); i = obj.getcompanion(7); obj.entities[i].tile = 6; obj.entities[i].state = 1; break; case 132: dwgfx.textboxremove(); hascontrol = true; advancetext = false; state = 0; break; case 200: //Init final stretch state++; music.playef(9, 10); //music.play(2); obj.flags[72] = 1; screenshake = 10; flashlight = 5; map.finalstretch = true; map.warpx = false; map.warpy = false; map.background = 6; map.final_colormode = true; map.final_colorframe = 1; startscript = true; newscript="finalterminal_finish"; state = 0; break; case 300: startscript = true; newscript="custom_"+customscript[0]; obj.removetrigger(300); state = 0; break; case 301: startscript = true; newscript="custom_"+customscript[1]; obj.removetrigger(301); state = 0; break; case 302: startscript = true; newscript="custom_"+customscript[2]; obj.removetrigger(302); state = 0; break; case 303: startscript = true; newscript="custom_"+customscript[3]; obj.removetrigger(303); state = 0; break; case 304: startscript = true; newscript="custom_"+customscript[4]; obj.removetrigger(304); state = 0; break; case 305: startscript = true; newscript="custom_"+customscript[5]; obj.removetrigger(305); state = 0; break; case 306: startscript = true; newscript="custom_"+customscript[6]; obj.removetrigger(306); state = 0; break; case 307: startscript = true; newscript="custom_"+customscript[7]; obj.removetrigger(307); state = 0; break; case 308: startscript = true; newscript="custom_"+customscript[8]; obj.removetrigger(308); state = 0; break; case 309: startscript = true; newscript="custom_"+customscript[9]; obj.removetrigger(309); state = 0; break; case 310: startscript = true; newscript="custom_"+customscript[10]; obj.removetrigger(310); state = 0; break; case 311: startscript = true; newscript="custom_"+customscript[11]; obj.removetrigger(311); state = 0; break; case 312: startscript = true; newscript="custom_"+customscript[12]; obj.removetrigger(312); state = 0; break; case 313: startscript = true; newscript="custom_"+customscript[13]; obj.removetrigger(313); state = 0; break; case 314: startscript = true; newscript="custom_"+customscript[14]; obj.removetrigger(314); state = 0; break; case 315: startscript = true; newscript="custom_"+customscript[15]; obj.removetrigger(315); state = 0; break; case 316: startscript = true; newscript="custom_"+customscript[16]; obj.removetrigger(316); state = 0; break; case 317: startscript = true; newscript="custom_"+customscript[17]; obj.removetrigger(317); state = 0; break; case 318: startscript = true; newscript="custom_"+customscript[18]; obj.removetrigger(318); state = 0; break; case 319: startscript = true; newscript="custom_"+customscript[19]; obj.removetrigger(319); state = 0; break; case 320: startscript = true; newscript="custom_"+customscript[20]; obj.removetrigger(320); state = 0; break; case 321: startscript = true; newscript="custom_"+customscript[21]; obj.removetrigger(321); state = 0; break; case 322: startscript = true; newscript="custom_"+customscript[22]; obj.removetrigger(322); state = 0; break; case 323: startscript = true; newscript="custom_"+customscript[23]; obj.removetrigger(323); state = 0; break; case 324: startscript = true; newscript="custom_"+customscript[24]; obj.removetrigger(324); state = 0; break; case 325: startscript = true; newscript="custom_"+customscript[25]; obj.removetrigger(325); state = 0; break; case 326: startscript = true; newscript="custom_"+customscript[26]; obj.removetrigger(326); state = 0; break; case 327: startscript = true; newscript="custom_"+customscript[27]; obj.removetrigger(327); state = 0; break; case 328: startscript = true; newscript="custom_"+customscript[28]; obj.removetrigger(328); state = 0; break; case 329: startscript = true; newscript="custom_"+customscript[29]; obj.removetrigger(329); state = 0; break; case 330: startscript = true; newscript="custom_"+customscript[30]; obj.removetrigger(330); state = 0; break; case 331: startscript = true; newscript="custom_"+customscript[31]; obj.removetrigger(331); state = 0; break; case 332: startscript = true; newscript="custom_"+customscript[32]; obj.removetrigger(332); state = 0; break; case 333: startscript = true; newscript="custom_"+customscript[33]; obj.removetrigger(333); state = 0; break; case 334: startscript = true; newscript="custom_"+customscript[34]; obj.removetrigger(334); state = 0; break; case 335: startscript = true; newscript="custom_"+customscript[35]; obj.removetrigger(335); state = 0; break; case 336: startscript = true; newscript="custom_"+customscript[36]; obj.removetrigger(336); state = 0; break; case 1000: dwgfx.showcutscenebars = true; hascontrol = false; completestop = true; state++; statedelay = 15; break; case 1001: //Found a trinket! advancetext = true; state++; if (dwgfx.flipmode) { dwgfx.createtextbox(" Congratulations! ", 50, 105, 174, 174, 174); dwgfx.addline(""); dwgfx.addline("You have found a shiny trinket!"); dwgfx.textboxcenterx(); if(map.custommode) { dwgfx.createtextbox(" " + help.number(trinkets) + " out of " + help.number(map.customtrinkets)+ " ", 50, 65, 174, 174, 174); dwgfx.textboxcenterx(); } else { dwgfx.createtextbox(" " + help.number(trinkets) + " out of Twenty ", 50, 65, 174, 174, 174); dwgfx.textboxcenterx(); } } else { dwgfx.createtextbox(" Congratulations! ", 50, 85, 174, 174, 174); dwgfx.addline(""); dwgfx.addline("You have found a shiny trinket!"); dwgfx.textboxcenterx(); if(map.custommode) { dwgfx.createtextbox(" " + help.number(trinkets) + " out of " + help.number(map.customtrinkets)+ " ", 50, 135, 174, 174, 174); dwgfx.textboxcenterx(); } else { dwgfx.createtextbox(" " + help.number(trinkets) + " out of Twenty ", 50, 135, 174, 174, 174); dwgfx.textboxcenterx(); } } break; case 1003: dwgfx.textboxremove(); hascontrol = true; advancetext = false; completestop = false; state = 0; //music.play(music.resumesong); if(!muted && music.currentsong>-1) music.fadeMusicVolumeIn(3000); dwgfx.showcutscenebars = false; break; case 1010: dwgfx.showcutscenebars = true; hascontrol = false; completestop = true; state++; statedelay = 15; break; case 1011: //Found a trinket! advancetext = true; state++; if (dwgfx.flipmode) { dwgfx.createtextbox(" Congratulations! ", 50, 105, 174, 174, 174); dwgfx.addline(""); dwgfx.addline("You have found a lost crewmate!"); dwgfx.textboxcenterx(); if(int(map.customcrewmates-crewmates)==0) { dwgfx.createtextbox(" All crewmates rescued! ", 50, 135, 174, 174, 174); } else if(map.customcrewmates-crewmates==1) { dwgfx.createtextbox(" " + help.number(int(map.customcrewmates-crewmates))+ " remains ", 50, 135, 174, 174, 174); } else { dwgfx.createtextbox(" " + help.number(int(map.customcrewmates-crewmates))+ " remain ", 50, 135, 174, 174, 174); } dwgfx.textboxcenterx(); } else { dwgfx.createtextbox(" Congratulations! ", 50, 85, 174, 174, 174); dwgfx.addline(""); dwgfx.addline("You have found a lost crewmate!"); dwgfx.textboxcenterx(); if(int(map.customcrewmates-crewmates)==0) { dwgfx.createtextbox(" All crewmates rescued! ", 50, 135, 174, 174, 174); } else if(map.customcrewmates-crewmates==1) { dwgfx.createtextbox(" " + help.number(int(map.customcrewmates-crewmates))+ " remains ", 50, 135, 174, 174, 174); } else { dwgfx.createtextbox(" " + help.number(int(map.customcrewmates-crewmates))+ " remain ", 50, 135, 174, 174, 174); } dwgfx.textboxcenterx(); } break; case 1013: dwgfx.textboxremove(); hascontrol = true; advancetext = false; completestop = false; state = 0; if(map.customcrewmates-crewmates==0) { if(map.custommodeforreal) { dwgfx.fademode = 2; if(!muted && ed.levmusic>0) music.fadeMusicVolumeIn(3000); if(ed.levmusic>0) music.fadeout(); state=1014; } else { gamestate = EDITORMODE; dwgfx.backgrounddrawn=false; if(!muted && ed.levmusic>0) music.fadeMusicVolumeIn(3000); if(ed.levmusic>0) music.fadeout(); } } else { if(!muted && ed.levmusic>0) music.fadeMusicVolumeIn(3000); } dwgfx.showcutscenebars = false; break; case 1014: frames--; if(dwgfx.fademode == 1) state++; break; case 1015: dwgfx.flipmode = false; gamestate = TITLEMODE; dwgfx.fademode = 4; music.play(6); dwgfx.backgrounddrawn = true; map.tdrawback = true; //Update level stats if(map.customcrewmates-crewmates==0) { //Finished level if(map.customtrinkets-trinkets==0) { //and got all the trinkets! updatecustomlevelstats(customlevelfilename, 3); } else { updatecustomlevelstats(customlevelfilename, 1); } } createmenu("levellist"); state = 0; break; case 2000: //Game Saved! if (intimetrial || nodeathmode || inintermission) { state = 0; } else { savetele(map, obj, music); if (dwgfx.flipmode) { dwgfx.createtextbox(" Game Saved ", -1, 202, 174, 174, 174); dwgfx.textboxtimer(25); } else { dwgfx.createtextbox(" Game Saved ", -1, 12, 174, 174, 174); dwgfx.textboxtimer(25); } state = 0; } break; case 2500: music.play(5); //Activating a teleporter (appear) state++; statedelay = 15; flashlight = 5; screenshake = 90; music.playef(9, 10); break; case 2501: //Activating a teleporter 2 state++; statedelay = 0; flashlight = 5; screenshake = 0; //we're done here! music.playef(10, 10); break; case 2502: //Activating a teleporter 2 state++; statedelay = 5; i = obj.getplayer(); obj.entities[i].colour = 0; obj.entities[i].invis = false; obj.entities[i].xp = obj.entities[obj.getteleporter()].xp+44; obj.entities[i].yp = obj.entities[obj.getteleporter()].yp+44; obj.entities[i].ay = -6; obj.entities[i].ax = 6; obj.entities[i].vy = -6; obj.entities[i].vx = 6; i = obj.getteleporter(); obj.entities[i].tile = 1; obj.entities[i].colour = 101; break; case 2503: state++; i = obj.getplayer(); obj.entities[i].xp += 10; break; case 2504: state++; i = obj.getplayer(); //obj.entities[i].xp += 10; break; case 2505: state++; i = obj.getplayer(); obj.entities[i].xp += 8; break; case 2506: state++; i = obj.getplayer(); obj.entities[i].xp += 6; break; case 2507: state++; i = obj.getplayer(); //obj.entities[i].xp += 4; break; case 2508: state++; i = obj.getplayer(); obj.entities[i].xp += 2; break; case 2509: state++; statedelay = 15; i = obj.getplayer(); obj.entities[i].xp += 1; break; case 2510: advancetext = true; hascontrol = false; dwgfx.createtextbox("Hello?", 125+24, 152-20, 164, 164, 255); state++; music.playef(11, 10); dwgfx.textboxactive(); break; case 2512: advancetext = true; hascontrol = false; dwgfx.createtextbox("Is anyone there?", 125+8, 152-24, 164, 164, 255); state++; music.playef(11, 10); dwgfx.textboxactive(); break; case 2514: dwgfx.textboxremove(); hascontrol = true; advancetext = false; state = 0; music.play(3); break; case 3000: //Activating a teleporter (long version for level complete) state++; statedelay = 30; flashlight = 5; screenshake = 90; music.playef(9, 10); break; case 3001: //Activating a teleporter 2 state++; statedelay = 15; flashlight = 5; music.playef(9, 10); break; case 3002: //Activating a teleporter 2 state++; statedelay = 15; flashlight = 5; music.playef(9, 10); break; case 3003: //Activating a teleporter 2 state++; statedelay = 15; flashlight = 5; music.playef(9, 10); break; case 3004: //Activating a teleporter 2 state++; statedelay = 0; flashlight = 5; screenshake = 0; //we're done here! music.playef(10, 10); break; case 3005: //Activating a teleporter 2 state++; statedelay = 50; //testing! //state = 3006; //Warp Zone //state = 3020; //Space Station switch(companion) { case 6: state = 3006; break; //Warp Zone case 7: state = 3020; break; //Space Station case 8: state = 3040; break; //Lab case 9: state = 3060; break; //Tower case 10: state = 3080; break; //Intermission 2 case 11: state = 3085; break; //Intermission 1 } i = obj.getplayer(); obj.entities[i].colour = 0; obj.entities[i].invis = true; i = obj.getcompanion(companion); if(i>-1) { obj.entities[i].active = false; } i = obj.getteleporter(); obj.entities[i].tile = 1; obj.entities[i].colour = 100; break; case 3006: //Level complete! (warp zone) unlocknum(4, map, dwgfx); lastsaved = 4; music.play(0); state++; statedelay = 75; if (dwgfx.flipmode) { dwgfx.createtextbox("", -1, 180, 165, 165, 255); } else { dwgfx.createtextbox("", -1, 12, 165, 165, 255); } //dwgfx.addline(" Level Complete! "); dwgfx.addline(" "); dwgfx.addline(""); dwgfx.addline(""); dwgfx.textboxcenterx(); /* advancetext = true; hascontrol = false; state = 3; dwgfx.createtextbox("To do: write quick", 50, 80, 164, 164, 255); dwgfx.addline("intro to story!");*/ break; case 3007: state++; statedelay = 45; if (dwgfx.flipmode) { dwgfx.createtextbox("", -1, 104, 175,174,174); } else { dwgfx.createtextbox("", -1, 64+8+16, 175,174,174); } dwgfx.addline(" You have rescued "); dwgfx.addline(" a crew member! "); dwgfx.addline(""); dwgfx.textboxcenterx(); break; case 3008: state++; statedelay = 45; temp = 6 - crewrescued(); if (temp == 1) { tempstring = " One remains "; if (dwgfx.flipmode) { dwgfx.createtextbox(tempstring, -1, 72, 174, 174, 174); } else { dwgfx.createtextbox(tempstring, -1, 128+16, 174, 174, 174); } } else if (temp > 0) { tempstring = " " + help.number(temp) + " remain "; if (dwgfx.flipmode) { dwgfx.createtextbox(tempstring, -1, 72, 174, 174, 174); } else { dwgfx.createtextbox(tempstring, -1, 128+16, 174, 174, 174); } } else { if (dwgfx.flipmode) { dwgfx.createtextbox(" All Crew Members Rescued! ", -1, 72, 174, 174, 174); } else { dwgfx.createtextbox(" All Crew Members Rescued! ", -1, 128+16, 174, 174, 174); } } dwgfx.textboxcenterx(); break; case 3009: state++; statedelay = 0; if (dwgfx.flipmode) { dwgfx.createtextbox(" Press ACTION to continue ", -1, 20, 164, 164, 255); } else { dwgfx.createtextbox(" Press ACTION to continue ", -1, 196, 164, 164, 255); } dwgfx.textboxcenterx(); break; case 3010: if (jumppressed) { state++; statedelay = 30; dwgfx.textboxremove(); } break; case 3011: state = 3070; statedelay = 0; break; case 3020: //Level complete! (Space Station 2) unlocknum(3, map, dwgfx); lastsaved = 2; music.play(0); state++; statedelay = 75; if (dwgfx.flipmode) { dwgfx.createtextbox("", -1, 180, 165, 165, 255); } else { dwgfx.createtextbox("", -1, 12, 165, 165, 255); } //dwgfx.addline(" Level Complete! "); dwgfx.addline(" "); dwgfx.addline(""); dwgfx.addline(""); dwgfx.textboxcenterx(); /* advancetext = true; hascontrol = false; state = 3; dwgfx.createtextbox("To do: write quick", 50, 80, 164, 164, 255); dwgfx.addline("intro to story!");*/ break; case 3021: state++; statedelay = 45; if (dwgfx.flipmode) { dwgfx.createtextbox("", -1, 104, 174,175,174); } else { dwgfx.createtextbox("", -1, 64+8+16, 174,175,174); } dwgfx.addline(" You have rescued "); dwgfx.addline(" a crew member! "); dwgfx.addline(""); dwgfx.textboxcenterx(); break; case 3022: state++; statedelay = 45; temp = 6 - crewrescued(); if (temp == 1) { tempstring = " One remains "; if (dwgfx.flipmode) { dwgfx.createtextbox(tempstring, -1, 72, 174, 174, 174); } else { dwgfx.createtextbox(tempstring, -1, 128+16, 174, 174, 174); } } else if (temp > 0) { tempstring = " " + help.number(temp) + " remain "; if (dwgfx.flipmode) { dwgfx.createtextbox(tempstring, -1, 72, 174, 174, 174); } else { dwgfx.createtextbox(tempstring, -1, 128+16, 174, 174, 174); } } else { if (dwgfx.flipmode) { dwgfx.createtextbox(" All Crew Members Rescued! ", -1, 72, 174, 174, 174); } else { dwgfx.createtextbox(" All Crew Members Rescued! ", -1, 128+16, 174, 174, 174); } } dwgfx.textboxcenterx(); break; case 3023: state++; statedelay = 0; if (dwgfx.flipmode) { dwgfx.createtextbox(" Press ACTION to continue ", -1, 20, 164, 164, 255); } else { dwgfx.createtextbox(" Press ACTION to continue ", -1, 196, 164, 164, 255); } dwgfx.textboxcenterx(); break; case 3024: if (jumppressed) { state++; statedelay = 30; dwgfx.textboxremove(); } break; case 3025: state = 3070; statedelay = 0; break; case 3040: //Level complete! (Lab) unlocknum(1, map, dwgfx); lastsaved = 5; music.play(0); state++; statedelay = 75; if (dwgfx.flipmode) { dwgfx.createtextbox("", -1, 180, 165, 165, 255); } else { dwgfx.createtextbox("", -1, 12, 165, 165, 255); } //dwgfx.addline(" Level Complete! "); dwgfx.addline(" "); dwgfx.addline(""); dwgfx.addline(""); dwgfx.textboxcenterx(); /* advancetext = true; hascontrol = false; state = 3; dwgfx.createtextbox("To do: write quick", 50, 80, 164, 164, 255); dwgfx.addline("intro to story!");*/ break; case 3041: state++; statedelay = 45; if (dwgfx.flipmode) { dwgfx.createtextbox("", -1, 104, 174,174,175); } else { dwgfx.createtextbox("", -1, 64+8+16, 174,174,175); } dwgfx.addline(" You have rescued "); dwgfx.addline(" a crew member! "); dwgfx.addline(""); dwgfx.textboxcenterx(); break; case 3042: state++; statedelay = 45; temp = 6 - crewrescued(); if (temp == 1) { tempstring = " One remains "; if (dwgfx.flipmode) { dwgfx.createtextbox(tempstring, -1, 72, 174, 174, 174); } else { dwgfx.createtextbox(tempstring, -1, 128+16, 174, 174, 174); } } else if (temp > 0) { tempstring = " " + help.number(temp) + " remain "; if (dwgfx.flipmode) { dwgfx.createtextbox(tempstring, -1, 72, 174, 174, 174); } else { dwgfx.createtextbox(tempstring, -1, 128+16, 174, 174, 174); } } else { if (dwgfx.flipmode) { dwgfx.createtextbox(" All Crew Members Rescued! ", -1, 72, 174, 174, 174); } else { dwgfx.createtextbox(" All Crew Members Rescued! ", -1, 128+16, 174, 174, 174); } } dwgfx.textboxcenterx(); break; case 3043: state++; statedelay = 0; if (dwgfx.flipmode) { dwgfx.createtextbox(" Press ACTION to continue ", -1, 20, 164, 164, 255); } else { dwgfx.createtextbox(" Press ACTION to continue ", -1, 196, 164, 164, 255); } dwgfx.textboxcenterx(); break; case 3044: if (jumppressed) { state++; statedelay = 30; dwgfx.textboxremove(); } break; case 3045: state = 3070; statedelay = 0; break; case 3050: //Level complete! (Space Station 1) unlocknum(0, map, dwgfx); lastsaved = 1; music.play(0); state++; statedelay = 75; if (dwgfx.flipmode) { dwgfx.createtextbox("", -1, 180, 165, 165, 255); } else { dwgfx.createtextbox("", -1, 12, 165, 165, 255); } //dwgfx.addline(" Level Complete! "); dwgfx.addline(" "); dwgfx.addline(""); dwgfx.addline(""); dwgfx.textboxcenterx(); /* advancetext = true; hascontrol = false; state = 3; dwgfx.createtextbox("To do: write quick", 50, 80, 164, 164, 255); dwgfx.addline("intro to story!");*/ break; case 3051: state++; statedelay = 45; if (dwgfx.flipmode) { dwgfx.createtextbox("", -1, 104, 175,175,174); } else { dwgfx.createtextbox("", -1, 64+8+16, 175,175,174); } dwgfx.addline(" You have rescued "); dwgfx.addline(" a crew member! "); dwgfx.addline(""); dwgfx.textboxcenterx(); break; case 3052: state++; statedelay = 45; temp = 6 - crewrescued(); if (temp == 1) { tempstring = " One remains "; if (dwgfx.flipmode) { dwgfx.createtextbox(tempstring, -1, 72, 174, 174, 174); } else { dwgfx.createtextbox(tempstring, -1, 128+16, 174, 174, 174); } } else if (temp > 0) { tempstring = " " + help.number(temp) + " remain "; if (dwgfx.flipmode) { dwgfx.createtextbox(tempstring, -1, 72, 174, 174, 174); } else { dwgfx.createtextbox(tempstring, -1, 128+16, 174, 174, 174); } } else { if (dwgfx.flipmode) { dwgfx.createtextbox(" All Crew Members Rescued! ", -1, 72, 174, 174, 174); } else { dwgfx.createtextbox(" All Crew Members Rescued! ", -1, 128+16, 174, 174, 174); } } dwgfx.textboxcenterx(); break; case 3053: state++; statedelay = 0; if (dwgfx.flipmode) { dwgfx.createtextbox(" Press ACTION to continue ", -1, 20, 164, 164, 255); } else { dwgfx.createtextbox(" Press ACTION to continue ", -1, 196, 164, 164, 255); } dwgfx.textboxcenterx(); break; case 3054: if (jumppressed) { state++; statedelay = 30; dwgfx.textboxremove(); crewstats[1] = 0; //Set violet's rescue script to 0 to make the next bit easier teleportscript = ""; } break; case 3055: dwgfx.fademode = 2; state++; statedelay = 10; break; case 3056: if(dwgfx.fademode==1) { startscript = true; if (nocutscenes) { newscript="bigopenworldskip"; } else { newscript = "bigopenworld"; } state = 0; } break; case 3060: //Level complete! (Tower) unlocknum(2, map, dwgfx); lastsaved = 3; music.play(0); state++; statedelay = 75; if (dwgfx.flipmode) { dwgfx.createtextbox("", -1, 180, 165, 165, 255); } else { dwgfx.createtextbox("", -1, 12, 165, 165, 255); } //dwgfx.addline(" Level Complete! "); dwgfx.addline(" "); dwgfx.addline(""); dwgfx.addline(""); dwgfx.textboxcenterx(); /* advancetext = true; hascontrol = false; state = 3; dwgfx.createtextbox("To do: write quick", 50, 80, 164, 164, 255); dwgfx.addline("intro to story!");*/ break; case 3061: state++; statedelay = 45; if (dwgfx.flipmode) { dwgfx.createtextbox("", -1, 104, 175,174,175); } else { dwgfx.createtextbox("", -1, 64+8+16, 175,174,175); } dwgfx.addline(" You have rescued "); dwgfx.addline(" a crew member! "); dwgfx.addline(""); dwgfx.textboxcenterx(); break; case 3062: state++; statedelay = 45; temp = 6 - crewrescued(); if (temp == 1) { tempstring = " One remains "; if (dwgfx.flipmode) { dwgfx.createtextbox(tempstring, -1, 72, 174, 174, 174); } else { dwgfx.createtextbox(tempstring, -1, 128+16, 174, 174, 174); } } else if (temp > 0) { tempstring = " " + help.number(temp) + " remain "; if (dwgfx.flipmode) { dwgfx.createtextbox(tempstring, -1, 72, 174, 174, 174); } else { dwgfx.createtextbox(tempstring, -1, 128+16, 174, 174, 174); } } else { if (dwgfx.flipmode) { dwgfx.createtextbox(" All Crew Members Rescued! ", -1, 72, 174, 174, 174); } else { dwgfx.createtextbox(" All Crew Members Rescued! ", -1, 128+16, 174, 174, 174); } } dwgfx.textboxcenterx(); break; case 3063: state++; statedelay = 0; if (dwgfx.flipmode) { dwgfx.createtextbox(" Press ACTION to continue ", -1, 20, 164, 164, 255); } else { dwgfx.createtextbox(" Press ACTION to continue ", -1, 196, 164, 164, 255); } dwgfx.textboxcenterx(); break; case 3064: if (jumppressed) { state++; statedelay = 30; dwgfx.textboxremove(); } break; case 3065: state = 3070; statedelay = 0; break; case 3070: dwgfx.fademode = 2; state++; break; case 3071: if (dwgfx.fademode == 1) state++; break; case 3072: //Ok, we need to adjust some flags based on who've we've rescued. Some of there conversation options //change depending on when they get back to the ship. if (lastsaved == 2) { if (crewstats[3]) obj.flags[25] = 1; if (crewstats[4]) obj.flags[26] = 1; if (crewstats[5]) obj.flags[24] = 1; } else if (lastsaved == 3) { if (crewstats[2]) obj.flags[50] = 1; if (crewstats[4]) obj.flags[49] = 1; if (crewstats[5]) obj.flags[48] = 1; } else if (lastsaved == 4) { if (crewstats[2]) obj.flags[54] = 1; if (crewstats[3]) obj.flags[55] = 1; if (crewstats[5]) obj.flags[56] = 1; } else if (lastsaved == 5) { if (crewstats[2]) obj.flags[37] = 1; if (crewstats[3]) obj.flags[38] = 1; if (crewstats[4]) obj.flags[39] = 1; } //We're pitch black now, make a decision companion = 0; if (crewrescued() == 6) { startscript = true; newscript="startlevel_final"; state = 0; } else if (crewrescued() == 4) { companion = 11; supercrewmate = true; scmprogress = 0; startscript = true; newscript = "intermission_1"; obj.flags[19] = 1; if (lastsaved == 2) obj.flags[32] = 1; if (lastsaved == 3) obj.flags[35] = 1; if (lastsaved == 4) obj.flags[34] = 1; if (lastsaved == 5) obj.flags[33] = 1; state = 0; } else if (crewrescued() == 5) { startscript = true; newscript = "intermission_2"; obj.flags[20] = 1; if (lastsaved == 2) obj.flags[32] = 1; if (lastsaved == 3) obj.flags[35] = 1; if (lastsaved == 4) obj.flags[34] = 1; if (lastsaved == 5) obj.flags[33] = 1; state = 0; } else { startscript = true; newscript="regularreturn"; state = 0; } break; case 3080: //returning from an intermission, very like 3070 if (inintermission) { dwgfx.fademode = 2; companion = 0; state=3100; } else { unlocknum(7, map, dwgfx); dwgfx.fademode = 2; companion = 0; state++; } break; case 3081: if (dwgfx.fademode == 1) state++; break; case 3082: map.finalmode = false; startscript = true; newscript="regularreturn"; state = 0; break; case 3085: //returning from an intermission, very like 3070 //return to menu from here if (inintermission) { companion = 0; supercrewmate = false; state++; dwgfx.fademode = 2; music.fadeout(); state=3100; } else { unlocknum(6, map, dwgfx); dwgfx.fademode = 2; companion = 0; supercrewmate = false; state++; } break; case 3086: if (dwgfx.fademode == 1) state++; break; case 3087: map.finalmode = false; startscript = true; newscript="regularreturn"; state = 0; break; case 3100: if(dwgfx.fademode == 1) state++; break; case 3101: dwgfx.flipmode = false; gamestate = 1; dwgfx.fademode = 4; dwgfx.backgrounddrawn = true; map.tdrawback = true; createmenu("play"); music.play(6); state = 0; break; //startscript = true; newscript="returntohub"; //state = 0; /*case 3025: if (recording == 1) { //if recording the input, output it to debug here trace(recordstring); help.toclipboard(recordstring); } test = true; teststring = recordstring; dwgfx.createtextbox(" Congratulations! ", 50, 80, 164, 164, 255); dwgfx.addline(""); dwgfx.addline("Your play of this level has"); dwgfx.addline("been copied to the clipboard."); dwgfx.addline(""); dwgfx.addline("Please consider pasting and"); dwgfx.addline("sending it to me! Even if you"); dwgfx.addline("made a lot of mistakes - knowing"); dwgfx.addline("exactly where people are having"); dwgfx.addline("trouble is extremely useful!"); dwgfx.textboxcenter(); state = 0; break;*/ case 3500: music.fadeout(); state++; statedelay = 120; //state = 3511; //testing break; case 3501: //Game complete! NETWORK_unlockAchievement("vvvvvvgamecomplete"); unlocknum(5, map, dwgfx); crewstats[0] = true; state++; statedelay = 75; music.play(7); if (dwgfx.flipmode) { dwgfx.createtextbox("", -1, 180, 164, 165, 255); } else { dwgfx.createtextbox("", -1, 12, 164, 165, 255); } dwgfx.addline(" "); dwgfx.addline(""); dwgfx.addline(""); dwgfx.textboxcenterx(); break; case 3502: state++; statedelay = 45+15; if (dwgfx.flipmode) { dwgfx.createtextbox(" All Crew Members Rescued! ", -1, 175-24, 0, 0, 0); } else { dwgfx.createtextbox(" All Crew Members Rescued! ", -1, 64, 0, 0, 0); } savetime = timestring(help); break; case 3503: state++; statedelay = 45; tempstring = help.number(trinkets); if (dwgfx.flipmode) { dwgfx.createtextbox("Trinkets Found:", 48, 155-24, 0,0,0); dwgfx.createtextbox(tempstring, 180, 155-24, 0, 0, 0); } else { dwgfx.createtextbox("Trinkets Found:", 48, 84, 0,0,0); dwgfx.createtextbox(tempstring, 180, 84, 0, 0, 0); } break; case 3504: state++; statedelay = 45+15; tempstring = savetime; if (dwgfx.flipmode) { dwgfx.createtextbox(" Game Time:", 64, 143-24, 0,0,0); dwgfx.createtextbox(tempstring, 180, 143-24, 0, 0, 0); } else { dwgfx.createtextbox(" Game Time:", 64, 96, 0,0,0); dwgfx.createtextbox(tempstring, 180, 96, 0, 0, 0); } break; case 3505: state++; statedelay = 45; if (dwgfx.flipmode) { dwgfx.createtextbox(" Total Flips:", 64, 116-24, 0,0,0); dwgfx.createtextbox(help.String(totalflips), 180, 116-24, 0, 0, 0); } else { dwgfx.createtextbox(" Total Flips:", 64, 123, 0,0,0); dwgfx.createtextbox(help.String(totalflips), 180, 123, 0, 0, 0); } break; case 3506: state++; statedelay = 45+15; if (dwgfx.flipmode) { dwgfx.createtextbox("Total Deaths:", 64, 104-24, 0,0,0); dwgfx.createtextbox(help.String(deathcounts), 180, 104-24, 0, 0, 0); } else { dwgfx.createtextbox("Total Deaths:", 64, 135, 0,0,0); dwgfx.createtextbox(help.String(deathcounts), 180, 135, 0, 0, 0); } break; case 3507: state++; statedelay = 45+15; if (dwgfx.flipmode) { tempstring = "Hardest Room (with " + help.String(hardestroomdeaths) + " deaths)"; dwgfx.createtextbox(tempstring, -1, 81-24, 0,0,0); dwgfx.createtextbox(hardestroom, -1, 69-24, 0, 0, 0); } else { tempstring = "Hardest Room (with " + help.String(hardestroomdeaths) + " deaths)"; dwgfx.createtextbox(tempstring, -1, 158, 0,0,0); dwgfx.createtextbox(hardestroom, -1, 170, 0, 0, 0); } break; case 3508: state++; statedelay = 0; if (dwgfx.flipmode) { dwgfx.createtextbox(" Press ACTION to continue ", -1, 20, 164, 164, 255); } else { dwgfx.createtextbox(" Press ACTION to continue ", -1, 196, 164, 164, 255); } dwgfx.textboxcenterx(); break; case 3509: if (jumppressed) { state++; statedelay = 30; dwgfx.textboxremove(); } break; case 3510: //Save stats and stuff here if (obj.flags[73] == 0) { //flip mode complete NETWORK_unlockAchievement("vvvvvvgamecompleteflip"); unlock[19] = true; } if (bestgamedeaths == -1) { bestgamedeaths = deathcounts; } else { if (deathcounts < bestgamedeaths) { bestgamedeaths = deathcounts; } } if (bestgamedeaths > -1) { if (bestgamedeaths <= 500) { NETWORK_unlockAchievement("vvvvvvcomplete500"); } if (bestgamedeaths <= 250) { NETWORK_unlockAchievement("vvvvvvcomplete250"); } if (bestgamedeaths <= 100) { NETWORK_unlockAchievement("vvvvvvcomplete100"); } if (bestgamedeaths <= 50) { NETWORK_unlockAchievement("vvvvvvcomplete50"); } } savestats(map, dwgfx); if (nodeathmode) { NETWORK_unlockAchievement("vvvvvvmaster"); //bloody hell unlock[20] = true; state = 3520; statedelay = 0; } else { statedelay = 120; state++; } break; case 3511: //Activating a teleporter (long version for level complete) i = obj.getplayer(); obj.entities[i].colour = 102; obj.flags[67] = 1; state++; statedelay = 30; flashlight = 5; screenshake = 90; music.playef(9, 10); break; case 3512: //Activating a teleporter 2 state++; statedelay = 15; flashlight = 5; music.playef(9, 10); break; case 3513: //Activating a teleporter 2 state++; statedelay = 15; flashlight = 5; music.playef(9, 10); break; case 3514: //Activating a teleporter 2 state++; statedelay = 15; flashlight = 5; music.playef(9, 10); break; case 3515: //Activating a teleporter 2 state++; statedelay = 0; flashlight = 5; screenshake = 0; i = obj.getplayer(); obj.entities[i].colour = 0; obj.entities[i].invis = true; //we're done here! music.playef(10, 10); statedelay = 60; break; case 3516: dwgfx.fademode = 2; state++; break; case 3517: if (dwgfx.fademode == 1) { state++; statedelay = 30; } break; case 3518: dwgfx.fademode = 4; state = 0; statedelay = 30; //music.play(5); //music.play(10); map.finalmode = false; map.final_colormode = false; map.final_mapcol = 0; map.final_colorframe = 0; map.finalstretch = false; map.finalx = 100; map.finaly = 100; dwgfx.cutscenebarspos = 320; teleport_to_new_area = true; teleportscript = "gamecomplete"; break; case 3520: //NO DEATH MODE COMPLETE JESUS hascontrol = false; crewstats[0] = true; dwgfx.fademode = 2; state++; break; case 3521: if(dwgfx.fademode == 1) state++; break; case 3522: dwgfx.flipmode = false; gamestate = 1; dwgfx.fademode = 4; dwgfx.backgrounddrawn = true; map.tdrawback = true; createmenu("nodeathmodecomplete"); state = 0; break; case 4000: //Activating a teleporter (short version) state++; statedelay = 10; flashlight = 5; screenshake = 10; music.playef(9, 10); break; case 4001: //Activating a teleporter 2 state++; statedelay = 0; flashlight = 5; screenshake = 0; //we're done here! music.playef(10, 10); break; case 4002: //Activating a teleporter 2 state++; statedelay = 10; //testing! //state = 3006; //Warp Zone //state = 3020; //Space Station //state = 3040; //Lab i = obj.getplayer(); obj.entities[i].colour = 0; obj.entities[i].invis = true; i = obj.getteleporter(); if(i>-1) { obj.entities[i].tile = 1; obj.entities[i].colour = 100; } break; case 4003: state = 0; statedelay = 0; teleport_to_new_area = true; break; case 4010: //Activating a teleporter (default appear) state++; statedelay = 15; flashlight = 5; screenshake = 90; music.playef(9, 10); break; case 4011: //Activating a teleporter 2 state++; statedelay = 0; flashlight = 5; screenshake = 0; music.playef(10, 10); break; case 4012: //Activating a teleporter 2 state++; statedelay = 5; i = obj.getplayer(); j = obj.getteleporter(); if (j != -1) { obj.entities[i].xp = obj.entities[j].xp+44; obj.entities[i].yp = obj.entities[j].yp+44; obj.entities[j].tile = 2; obj.entities[j].colour = 101; } obj.entities[i].colour = 0; obj.entities[i].invis = false; obj.entities[i].dir = 1; obj.entities[i].ay = -6; obj.entities[i].ax = 6; obj.entities[i].vy = -6; obj.entities[i].vx = 6; break; case 4013: state++; i = obj.getplayer(); obj.entities[i].xp += 10; break; case 4014: state++; i = obj.getplayer(); obj.entities[i].xp += 10; break; case 4015: state++; i = obj.getplayer(); obj.entities[i].xp += 8; break; case 4016: state++; i = obj.getplayer(); obj.entities[i].xp += 6; break; case 4017: state++; i = obj.getplayer(); obj.entities[i].xp += 3; break; case 4018: state++; statedelay = 15; i = obj.getplayer(); obj.entities[i].xp += 1; break; case 4019: if (intimetrial || nodeathmode || inintermission) { } else { savetele(map, obj, music); } i = obj.getteleporter(); activetele = true; teleblock.x = obj.entities[i].xp - 32; teleblock.y = obj.entities[i].yp - 32; teleblock.w = 160; teleblock.h = 160; hascontrol = true; advancetext = false; state = 0; break; case 4020: //Activating a teleporter (default appear) state++; statedelay = 15; flashlight = 5; screenshake = 90; music.playef(9, 10); break; case 4021: //Activating a teleporter 2 state++; statedelay = 0; flashlight = 5; screenshake = 0; music.playef(10, 10); break; case 4022: //Activating a teleporter 2 state++; statedelay = 5; i = obj.getplayer(); j = obj.getteleporter(); if (j != -1) { obj.entities[i].xp = obj.entities[j].xp+44; obj.entities[i].yp = obj.entities[j].yp+44; obj.entities[j].tile = 2; obj.entities[j].colour = 101; } obj.entities[i].colour = 0; obj.entities[i].invis = false; obj.entities[i].dir = 1; obj.entities[i].ay = -6; obj.entities[i].ax = 6; obj.entities[i].vy = -6; obj.entities[i].vx = 6; break; case 4023: state++; i = obj.getplayer(); obj.entities[i].xp += 12; break; case 4024: state++; i = obj.getplayer(); obj.entities[i].xp += 12; break; case 4025: state++; i = obj.getplayer(); obj.entities[i].xp += 10; break; case 4026: state++; i = obj.getplayer(); obj.entities[i].xp += 8; break; case 4027: state++; i = obj.getplayer(); obj.entities[i].xp += 5; break; case 4028: state++; statedelay = 15; i = obj.getplayer(); obj.entities[i].xp += 2; break; case 4029: hascontrol = true; advancetext = false; state = 0; break; case 4030: //Activating a teleporter (default appear) state++; statedelay = 15; flashlight = 5; screenshake = 90; music.playef(9, 10); break; case 4031: //Activating a teleporter 2 state++; statedelay = 0; flashlight = 5; screenshake = 0; music.playef(10, 10); break; case 4032: //Activating a teleporter 2 state++; statedelay = 5; i = obj.getplayer(); j = obj.getteleporter(); if (j != -1) { obj.entities[i].xp = obj.entities[j].xp+44; obj.entities[i].yp = obj.entities[j].yp+44; obj.entities[j].tile = 2; obj.entities[j].colour = 101; } obj.entities[i].colour = 0; obj.entities[i].invis = false; obj.entities[i].dir = 0; obj.entities[i].ay = -6; obj.entities[i].ax = -6; obj.entities[i].vy = -6; obj.entities[i].vx = -6; break; case 4033: state++; i = obj.getplayer(); obj.entities[i].xp -= 12; break; case 4034: state++; i = obj.getplayer(); obj.entities[i].xp -= 12; break; case 4035: state++; i = obj.getplayer(); obj.entities[i].xp -= 10; break; case 4036: state++; i = obj.getplayer(); obj.entities[i].xp -= 8; break; case 4037: state++; i = obj.getplayer(); obj.entities[i].xp -= 5; break; case 4038: state++; statedelay = 15; i = obj.getplayer(); obj.entities[i].xp -= 2; break; case 4039: hascontrol = true; advancetext = false; state = 0; break; case 4040: //Activating a teleporter (default appear) state++; statedelay = 15; flashlight = 5; screenshake = 90; music.playef(9, 10); break; case 4041: //Activating a teleporter 2 state++; statedelay = 0; flashlight = 5; screenshake = 0; music.playef(10, 10); break; case 4042: //Activating a teleporter 2 state++; statedelay = 5; i = obj.getplayer(); j = obj.getteleporter(); if (j != -1) { obj.entities[i].xp = obj.entities[j].xp+44; obj.entities[i].yp = obj.entities[j].yp+44; obj.entities[j].tile = 2; obj.entities[j].colour = 101; } obj.entities[i].colour = 0; obj.entities[i].invis = false; obj.entities[i].dir = 1; obj.entities[i].ay = -6; obj.entities[i].ax = 6; obj.entities[i].vy = -6; obj.entities[i].vx = 6; break; case 4043: state++; i = obj.getplayer(); obj.entities[i].xp += 12; obj.entities[i].yp -= 15; break; case 4044: state++; i = obj.getplayer(); obj.entities[i].xp += 12; obj.entities[i].yp -= 10; break; case 4045: state++; i = obj.getplayer(); obj.entities[i].xp += 12; obj.entities[i].yp -= 10; break; case 4046: state++; i = obj.getplayer(); obj.entities[i].xp += 8; obj.entities[i].yp -= 8; break; case 4047: state++; i = obj.getplayer(); obj.entities[i].xp += 6; obj.entities[i].yp -= 8; break; case 4048: state++; statedelay = 15; i = obj.getplayer(); obj.entities[i].xp += 3; break; case 4049: hascontrol = true; advancetext = false; state = 0; break; case 4050: //Activating a teleporter (default appear) state++; statedelay = 15; flashlight = 5; screenshake = 90; music.playef(9, 10); break; case 4051: //Activating a teleporter 2 state++; statedelay = 0; flashlight = 5; screenshake = 0; music.playef(10, 10); break; case 4052: //Activating a teleporter 2 state++; statedelay = 5; i = obj.getplayer(); j = obj.getteleporter(); if (j != -1) { obj.entities[i].xp = obj.entities[j].xp+44; obj.entities[i].yp = obj.entities[j].yp+44; obj.entities[j].tile = 2; obj.entities[j].colour = 101; } obj.entities[i].colour = 0; obj.entities[i].invis = false; obj.entities[i].dir = 1; obj.entities[i].ay = -6; obj.entities[i].ax = 6; obj.entities[i].vy = -6; obj.entities[i].vx = 6; break; case 4053: state++; i = obj.getplayer(); obj.entities[i].xp += 4; obj.entities[i].yp -= 15; break; case 4054: state++; i = obj.getplayer(); obj.entities[i].xp += 4; obj.entities[i].yp -= 10; break; case 4055: state++; i = obj.getplayer(); obj.entities[i].xp += 4; obj.entities[i].yp -= 10; break; case 4056: state++; i = obj.getplayer(); obj.entities[i].xp += 4; obj.entities[i].yp -= 8; break; case 4057: state++; i = obj.getplayer(); obj.entities[i].xp += 2; obj.entities[i].yp -= 8; break; case 4058: state++; statedelay = 15; i = obj.getplayer(); obj.entities[i].xp += 1; break; case 4059: hascontrol = true; advancetext = false; state = 0; break; case 4060: //Activating a teleporter (default appear) state++; statedelay = 15; flashlight = 5; screenshake = 90; music.playef(9, 10); break; case 4061: //Activating a teleporter 2 state++; statedelay = 0; flashlight = 5; screenshake = 0; music.playef(10, 10); break; case 4062: //Activating a teleporter 2 state++; statedelay = 5; i = obj.getplayer(); j = obj.getteleporter(); if (j != -1) { obj.entities[i].xp = obj.entities[j].xp+44; obj.entities[i].yp = obj.entities[j].yp+44; obj.entities[j].tile = 2; obj.entities[j].colour = 101; } obj.entities[i].colour = 0; obj.entities[i].invis = false; obj.entities[i].dir = 0; obj.entities[i].ay = -6; obj.entities[i].ax = -6; obj.entities[i].vy = -6; obj.entities[i].vx = -6; break; case 4063: state++; i = obj.getplayer(); obj.entities[i].xp -= 28; obj.entities[i].yp -= 8; break; case 4064: state++; i = obj.getplayer(); obj.entities[i].xp -= 28; obj.entities[i].yp -= 8; break; case 4065: state++; i = obj.getplayer(); obj.entities[i].xp -= 25; break; case 4066: state++; i = obj.getplayer(); obj.entities[i].xp -= 25; break; case 4067: state++; i = obj.getplayer(); obj.entities[i].xp -= 20; break; case 4068: state++; statedelay = 15; i = obj.getplayer(); obj.entities[i].xp -= 16; break; case 4069: hascontrol = true; advancetext = false; state = 0; break; case 4070: //Activating a teleporter (special for final script, player has colour changed to match rescued crewmate) state++; statedelay = 15; flashlight = 5; screenshake = 90; music.playef(9, 10); break; case 4071: //Activating a teleporter 2 state++; statedelay = 0; flashlight = 5; screenshake = 0; music.playef(10, 10); break; case 4072: //Activating a teleporter 2 state++; statedelay = 5; i = obj.getplayer(); j = obj.getteleporter(); if (j != -1) { obj.entities[i].xp = obj.entities[j].xp+44; obj.entities[i].yp = obj.entities[j].yp+44; obj.entities[j].tile = 2; obj.entities[j].colour = 101; } obj.entities[i].invis = false; obj.entities[i].dir = 1; obj.entities[i].colour = obj.crewcolour(lastsaved); obj.entities[i].ay = -6; obj.entities[i].ax = 6; obj.entities[i].vy = -6; obj.entities[i].vx = 6; break; case 4073: state++; i = obj.getplayer(); obj.entities[i].xp += 10; break; case 4074: state++; i = obj.getplayer(); obj.entities[i].xp += 10; break; case 4075: state++; i = obj.getplayer(); obj.entities[i].xp += 8; break; case 4076: state++; i = obj.getplayer(); obj.entities[i].xp += 6; break; case 4077: state++; i = obj.getplayer(); obj.entities[i].xp += 3; break; case 4078: state++; statedelay = 15; i = obj.getplayer(); obj.entities[i].xp += 1; break; case 4079: state = 0; startscript = true; newscript = "finallevel_teleporter"; break; case 4080: //Activating a teleporter (default appear) state++; statedelay = 15; flashlight = 5; screenshake = 90; music.playef(9, 10); break; case 4081: //Activating a teleporter 2 state++; statedelay = 0; flashlight = 5; screenshake = 0; music.playef(10, 10); break; case 4082: //Activating a teleporter 2 state++; statedelay = 5; i = obj.getplayer(); j = obj.getteleporter(); if (j != -1) { obj.entities[i].xp = obj.entities[j].xp+44; obj.entities[i].yp = obj.entities[j].yp+44; obj.entities[j].tile = 2; obj.entities[j].colour = 101; } obj.entities[i].colour = 0; obj.entities[i].invis = false; obj.entities[i].dir = 1; obj.entities[i].ay = -6; obj.entities[i].ax = 6; obj.entities[i].vy = -6; obj.entities[i].vx = 6; break; case 4083: state++; i = obj.getplayer(); obj.entities[i].xp += 10; break; case 4084: state++; i = obj.getplayer(); obj.entities[i].xp += 10; break; case 4085: state++; i = obj.getplayer(); obj.entities[i].xp += 8; break; case 4086: state++; i = obj.getplayer(); obj.entities[i].xp += 6; break; case 4087: state++; i = obj.getplayer(); obj.entities[i].xp += 3; break; case 4088: state++; statedelay = 15; i = obj.getplayer(); obj.entities[i].xp += 1; break; case 4089: startscript = true; newscript = "gamecomplete_ending"; state = 0; break; case 4090: //Activating a teleporter (default appear) state++; statedelay = 15; flashlight = 5; screenshake = 90; music.playef(9, 10); break; case 4091: //Activating a teleporter 2 state++; statedelay = 0; flashlight = 5; screenshake = 0; music.playef(10, 10); break; case 4092: //Activating a teleporter 2 state++; statedelay = 5; i = obj.getplayer(); j = obj.getteleporter(); if (j != -1) { obj.entities[i].xp = obj.entities[j].xp+44; obj.entities[i].yp = obj.entities[j].yp+44; obj.entities[j].tile = 2; obj.entities[j].colour = 101; } obj.entities[i].colour = 0; obj.entities[i].invis = false; obj.entities[i].dir = 1; obj.entities[i].ay = -6; obj.entities[i].ax = 6; obj.entities[i].vy = -6; obj.entities[i].vx = 6; break; case 4093: state++; i = obj.getplayer(); obj.entities[i].xp += 10; break; case 4094: state++; i = obj.getplayer(); obj.entities[i].xp += 10; break; case 4095: state++; i = obj.getplayer(); obj.entities[i].xp += 8; break; case 4096: state++; i = obj.getplayer(); obj.entities[i].xp += 6; break; case 4097: state++; i = obj.getplayer(); obj.entities[i].xp += 3; break; case 4098: state++; statedelay = 15; i = obj.getplayer(); obj.entities[i].xp += 1; break; case 4099: if (nocutscenes) { startscript = true; newscript = "levelonecompleteskip"; } else { startscript = true; newscript = "levelonecomplete_ending"; } state = 0; break; }

Cue the Will Ferrel meme: "I'm not even mad, that's impressive." Thanks to Andy K for bringing this to our attention.

[Advertisement] Continuously monitor your servers for configuration changes, and report when there's configuration drift. Get started with Otter today!
Read the whole story
elwillow
584 days ago
reply
Ottawa, Ontario
Share this story
Delete

Control Group

3 Comments and 17 Shares
Placeble 228 x/6
⬜⬜⬜⬜⬜
⬜⬜⬜⬜⬜
⬜⬜⬜⬜⬜
⬜⬜⬜⬜⬜
⬜⬜⬜⬜⬜
⬜⬜⬜⬜⬜
Read the whole story
popular
605 days ago
reply
elwillow
605 days ago
reply
Ottawa, Ontario
Share this story
Delete
3 public comments
philipstorry
605 days ago
reply
I've suddenly realised that I have been in SO MANY control groups in my life...
And it feels fine. 😉
London, United Kingdom
jlvanderzwan
605 days ago
All these things that turned out to have no benefit and a ton of annoying side-effects that we didn't have to deal with!
cjheinz
605 days ago
reply
Nice! Really takes the pressure off!
ossiander
605 days ago
reply
Works for me
Next Page of Stories