• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Memory hack

Level 19
Joined
Dec 12, 2010
Messages
2,069
dont speak for 2016 as for 2008. there's still even .24 clients, not to speak of separation between 26 and 27 versions.
there's 0 reasons to update, so I will force 1.26 as last stable patch which provides everything I need. rest is up to player. I dont care about splitting at all, people chose what do they want - officially but broken and useless patch or non-official new-life for the game. like heroes 3 HD vs oldschoold heroes 3 HD by baratorch
 
dont speak for 2016 as for 2008. there's still even .24 clients, not to speak of separation between 26 and 27 versions.
there's 0 reasons to update, so I will force 1.26 as last stable patch which provides everything I need. rest is up to player. I dont care about splitting at all, people chose what do they want - officially but broken and useless patch or non-official new-life for the game. like heroes 3 HD vs oldschoold heroes 3 HD by baratorch

Uh, so yeah good luck, though may I re-ask something if you have any spare time? Could you list the features this does or does better that WC3 can't normally do without? If you wouldn't mind, I would really appreciate it because this is quite an achievement in WC3 modding.
 
Level 19
Joined
Dec 12, 2010
Messages
2,069
how you gonna edit simple attack speed limit? movespeed? instant turn? unit coloring? playing sound, saving data onto hard drive, make use of mouse API, calling ingame functions like simulate unit attack, reset attack, and thats just a bit of those I need. creative people could use tons more vectors to work with. But I guess they wont do it here
 
how you gonna edit simple attack speed limit? movespeed? instant turn? unit coloring? playing sound, saving data onto hard drive, make use of mouse API, calling ingame functions like simulate unit attack, reset attack, and thats just a bit of those I need. creative people could use tons more vectors to work with. But I guess they wont do it here

You can only edit the memory right or can you add to it as in add new natives? Every one of those you suggested that I can understand is possible so far without this, just not as easy/smooth as this code-wise most likely. Though there has been a question that has been bugging me for a while now, can you force local files to be turned on through this?

The ones I didn't understand was what you meant by mouse API, simulate unit attack and reset attack.
 
Level 13
Joined
Mar 24, 2013
Messages
1,105
Take a look at the function list posted in the OP? It has a long list of things he has sorted through so far.

Rather than having to "fake" things via dummy abilities, units, etc. You are getting to change things directly.

There are plenty of things that you really cannot do nicely, that this easily can accomplish.
 
Take a look at the function list posted in the OP? It has a long list of things he has sorted through so far.

Rather than having to "fake" things via dummy abilities, units, etc. You are getting to change things directly.

There are plenty of things that you really cannot do nicely, that this easily can accomplish.

I'd rather duplicate as much as possible without this method if possible because I am making a map that I want to be accessed by anyone. This is indeed amazing, just I want to find out what it can do I normally couldn't, I did check his long list however there really isn't much info in there about the really unique stuff which is why I am here asking questions to try to promote the use of this and find out if I want to use it as well as well maybe explain some stuff for other potential users/players of this.

Though now that I think about it, I might have looked at a wrong list... Will check again later today to check what's public so far.
 
Level 13
Joined
Mar 24, 2013
Messages
1,105
function SetUnitAttackRange1 takes unit u, real r returns nothing
function
GetUnitAttackRange1 takes unit u returns real
function
SetUnitAttackRange2 takes unit u, real r returns nothing
function
GetUnitAttackRange2 takes unit u returns real

Various batching with WinAPI, allowing to open http addresses, run from CMD, writing/reading files, exporting files from MPQ, etc

I'd say yes to both.
 

Ralle

Owner
Level 77
Joined
Oct 6, 2004
Messages
10,098
The return bug should of been a short term hack as well but somehow it lasted for years.
Things have changed. Blizzard are officially supporting Wc3 again with patches. I know it may seem like they are at a stand still I am absolutely certain that they are working on something cool ;). This hack will be an even smaller niche before you know it.
 
Level 13
Joined
Oct 18, 2013
Messages
691
Things have changed. Blizzard are officially supporting Wc3 again with patches. I know it may seem like they are at a stand still I am absolutely certain that they are working on something cool ;). This hack will be an even smaller niche before you know it.

True, but for the moment, this is the BEST thing we have. Ideally, 1.28 will have most (if not all) of these functions, so that MemoryHack Maps aren't broken by future patch. Anything else is just Blizzard disrespecting the sheer work put into this.
 
Level 19
Joined
Dec 12, 2010
Messages
2,069
Ohhh blizz can do something cool, heh? like, Really? It's totally NOT PLAYER ORIENTED company, and I can easily show you that.

What did they do to make Warcraft 3 more popular? Cool events? There were nothing like blizz supporting the game on tournaments, getting prize pool by themselves, being involved into anything. Reason is - WOW, which took 100% priority and become one of reasons of Blizz North leaving.

Icefrog and tons of other mapmakers begging them for years to increase size limit from 4 to 8 mb. It took about 6 years since the release. Any reason to be that stubborn? None. Because it's NOT PLAYER ORIENTED COMPANY.

It's almost 2017 and there are been 0 fixes to game API. For instance motherfucking leaks for GUI players. As it may seems, GUI mapmakers - it's the very first players who want to keep game alive FOR FREE. Creating stuff Blizz didn't. But guess what, once they started, there's leaks. I can understand if that would be 1.03 or 1.04 patch, but it's already FUCKIN 1.26 (1.27 can't be counted as patch at all) and YET people struggling to make their maps running better than 30fps (PS4 masterrace). Guess what, fixing broken GUI actions could be done way back to 200x, yet nobody cared. Because it's NOT PLAYER ORIENTED COMPANY.

New API. Fuck, HTTP is luxury, OK, it makes little sense to keep it out. But. Fucking. Setters and getters. They could do that anytime, literally anywhere, allowing whole unit's structure to be visible and touchable. Ability's data table is amazing field for endless experiments. Same goes even for effects. None? None. Because it's NOT PLAYER ORIENTED COMPANY.

With the new fucniionality we can finally say "fuck u" and deploy All the fixes automatically, without involving users at all. Upload our new patch directly to them via simple map "wc3.5". Fix most of engine flaws by ourselves (for instance - unstackble buffs, hardcoded targets, inner bugs with impementation of some skills or/and object fields). Yet there's false prophets of "blizz will make stuff better, just wait". Nah. Im russian, I heard that 10000+ times, and believe me - it never comes true. And one of reasons of that - because blizzard is NOT PLAYER ORIENTED COMPANY.
 
Level 19
Joined
Dec 12, 2010
Messages
2,069
? I dont need any patch anymore. I have everything dota may need. Unless they gonna rewrite engine to support multithreading there nothing they can ever improve. Not to speak that PDF been heavily shorten and miss many good stuff.

The only reason I put this on air been other devs who may use it. Obviously there's still way too many blizz' zealots. IDC how you or anyone will react, posted it cause people asked to. Time will show anyway.
 
I have lots of respect for what you and the other guys have done to achieve such an API enhancement and share it with the community,
but it's still proprietary software by an existing company. And we should speak it good and welcome it if they work on it here and there,
even it for sure won't get to the same level of freedom that the memory hack gives us.
You personaly are happy with using this kind of dark way of memory analysing... still, globaly for the Warcraft community it's still also good if the company itself makes small and official changes, and you and we all should also respect that, too, I believe.:)
 
Last edited:

Deleted member 219079

D

Deleted member 219079

I dont need any patch anymore
I don't need patch either, but it'd be amazing if the small classic games team could attract people into WC3. I'd say these'd do: fix b.net delay, hacker report system, hide empty lobbies and b.net launcher entry. Then maybe the 16:9 ratio could give illusion of newness to returning ppl and maybe have them stay.

Patch 1.27 exceeded my expectations to be honest (guess I'm the only one):
wc3 1.27 wish list
  • wc3 1.27

------

multithreading
???
 
Level 19
Joined
Dec 12, 2010
Messages
2,069
we should speak it good and welcome it if they work on it here and there
nope, I definitely can't remember myself being obliged to anyone. They just earns money with China, nothing more. Issue is not just 1.27 fiasco, which would come away unnoticable, but the hype they sow with the video. Remember?

Aaaand why? 'Cause of managers! The people who stands behind dev team and arraging tasks for them, looking at metrics like "media attention" and shit. "We have nothing to show for now. Lets make fucking epic trailer of what we gonna do eventually!". So they pissed off community as hell. Sure, they just making money. But what kind of respect people here awaits for then?

It's definitely not 2005, when Blizz been on top. Today they ARE at the top, but in different niches - card games and MOBA, not RTS.

Patch 1.27 exceeded my expectations to be honest (guess I'm the only one):
it's soo good they managed to change the number, break back-compatibility doing no changes at all. sooo fucking good.

But even so, thing which buzz me the most - secrets! It's 2017, RTS barely competitive for years, yet DevTeam doesn't say what do they do. What's their targets. I dont need that bullshit again about revolutionary changes, please, show the real taskflow and things which are prioritized. Nope. Looks like there 10+ rivals for WC3 place.. oh I wish there been anyone. What the point hiding? None. Just typical big company stuff NOT PLAYER ORIENTED.

Oh one more thing I've missed. SC2. Widely awaited sequel which fault miserably regardless all hype Blizz tried to do. Well, maybe as ESport its not that dead, i dont know, but I've never seen it being top5 watched on streams. Anyway, main point it how they worked on that. They destroyed old Bnet and replaced it with Bnet 2.0, which killed all mapmaking community for obvious reasons. They claimed their rights onto everything you do in the editor. They kept retarded file size limits on maps and it's content. They made big step back from the community to the money. Can't see why would WC3 go different way. Like, why?
 
Last edited:
nope, I definitely can't remember myself being obliged to anyone
But the solution is to turn our back on blizzard, and leave them completly because the company does not what we want?
Even the mem heck provides everything, but it's like you're thinking a bit too radicaly and expropriate warcraft 3 to gaming community.

Don't you think it's also their right they focused on other games like WoW and such, which made bigger profits for the company?
It's sad they have not really worked much on wc3 for long years, and it's very awesome the community ( you rus guys) managed to work on their own as result,
but I don't get the anger towards blizzard, when they announce even a bit changes here and there. I still respect that..
 
Level 19
Joined
Dec 12, 2010
Messages
2,069
But the solution is to turn our back on blizzard, and leave them completly because the company does not what we want?
nope, they earning money, I understand that. So does EA, and I wont buy any shit of them. They dont care about satisfaction but numbers. Sure its just business, just like any other, and BlizzNorth weren't much player-oriented either. Thing is, market changed a lot. Look at dota2, who took majority of wc3 players - it's changes are still being influenced by players. Surely they make tons of money from the game, yet they managed to enlarge player base almost everytime they patch it. Satisfied player turns into more money, thats simple. As much as I dont like microtransactions they does work.
today you can't just push game into somebody's face. reaction is strong. Thing is, Blizz didnt even bothered with reaction at all, until breakpoint with 1.27. then SUDDENLY they came to hive, but yet didn't interact with anyone but heads, covering stuff like TOP SECRET. That's been key move yet their reliability heavily questioned.

and btw, it's not programmers who makes decision, its PM's work. and I think he does it bad. very bad so far.
 
Level 9
Joined
Jun 21, 2012
Messages
432
I am in favor of the patches, but to be honest blizzard takes too much time to offer us something new and also something that exceeds the expectations.... many guys have been writing Jass snippets for years and were expecting something like this, at least on the part of Blizzard of course.

Maybe not everyone will not see MemoryHack useful, some because they write their maps in GUI (or do not know how to use Jass) and others by security risk... But at least for me (and other users) it is.

01-png.254266

Blizzard 1.27 patch

I would like to request a little more support for MemoryHack. Sincerely do not understand why @Ralle do not support this, he will have his reasons and his position is valid and respectable...

g8yd1.jpg

(Edit by Obama)

 

Attachments

  • 01.png
    01.png
    145.6 KB · Views: 583

deepstrasz

Map Reviewer
Level 69
Joined
Jun 4, 2009
Messages
18,836
All in all, the World Editor has maybe been the (only) thing that could actually let players create their own games. Other editors for games like Unreal Tournament, Half-Life, mostly had specific limitations to the engine of those games. It took fans to create a proper Heroes of Might & Magic editor and implement random map generation for HoMM V which then the company integrated in the final game's release without any enhancements. Many other games, like Armies of Exigo, have an editor either specially designed for developers or for people who know a great deal of programming.
 

pyf

pyf

Level 32
Joined
Mar 21, 2016
Messages
2,985
...or maybe they wanted to be on par with a feature introduced in the StarCraft 1.16.1 patch.

Quoting:
"In-game Speed Options menu now has a "Enable CPU Throttling" checkbox. Enabling this option will allow StarCraft to consume fewer CPU cycles. By default this option is off."

I do not believe there was any compiler change with the 1.16.1 patch.
 

EdgeOfChaos

E

EdgeOfChaos

You might not care about whatever future patch is coming for WC3, but most other people do. If they release a good patch, I'm sure nearly everyone in the current WC3 community would update. I really can't understand your hostility to the idea that Blizzard might release a new patch...

At the very least, I could see Blizzard fixing up some of the more blatant bugs in the World Editor.
 
Level 8
Joined
Jan 23, 2015
Messages
121
Draco, it's about 1.27b, not the 1.27 itself. 1.27b is not released yet.
Here's a fresh screenshot of bnet forum: (https://us.battle.net/forums/en/bnet/topic/20752496109)
%D0%A1%D0%BD%D0%B8%D0%BC%D0%BE%D0%BA-jpg.255071

So it is said that we'll see all promised changes soon, even before Christmas.

(1.27b released 9 hours ago)
EDIT: Draco, did you say it in offence of released 1.27b?
 

Attachments

  • Снимок.JPG
    Снимок.JPG
    67.6 KB · Views: 424
Last edited:
  • Like
Reactions: pyf
Level 8
Joined
Jan 23, 2015
Messages
121
nah, ok, I'm slow a bit.

Specific Changes & Improvements
- Bug fixes and General Maintenance
- Raised file size limit from 8mb to 128mb.
- Added Script Verify to World Editor
- Custom blp files will no longer crash on Mac
(https://us.battle.net/forums/en/bnet/topic/20752625996)

I don't know what to say about it, as it is not full patch log, but it looks better than 1.27 after all because of x16 map size limit raise.
 
Level 8
Joined
Jan 23, 2015
Messages
121
This has been patched and is no longer working.
So memhack isn't working with this patch?
Ok then, I'm just in no need of it - staying with all that features Draco provided since it's much easier for me to develop several core features in my current project

EDIT: maddeem, how did you mentioned it out?
New patch can change adresses of variables, so it's only matter of time that it will be fixed IF Blizz didn't fix typecast.
 
Last edited:
Level 13
Joined
Nov 7, 2014
Messages
571
This has been patched and is no longer working.

The Typecast library seems to work so its only temporary (I guess, until leandrotp & friends decide to release a Version that works with it =)):

JASS:
library Typecast

globals
    code Code //This is not used, it's here just to fool Jasshelper
    code l__Code
    integer Int //This is not used, it's here just to fool Jasshelper
    integer l__Int
    string Str //This is not used, it's here just to fool Jasshelper
    string l__Str
    boolean Bool //This is not used, it's here just to fool Jasshelper
    boolean l__Bool
endglobals

function setCode takes code c returns nothing
    set l__Code = c
    return //Prevents Jasshelper from inlining this function
endfunction

function setInt takes integer i returns nothing
    set l__Int = i
    return //Prevents JassHelper from inlining this function
endfunction

function setStr takes string s returns nothing
    set l__Str = s
    return //Prevents JassHelper from inlining this function
endfunction

function setBool takes boolean b returns nothing
    set l__Bool = b
    return //Prevents Jasshelper from inlining this function
endfunction

private function Typecast1 takes nothing returns nothing
    local integer Code //Jasshelper will implicitly rename this to l__Code
    local code Int     //Jasshelper will implicitly rename this to l__Int
endfunction

//# +nosemanticerror
function C2I takes code c returns integer
    call setCode(c)
    return l__Code
endfunction

//# +nosemanticerror
function I2C takes integer i returns code
    call setInt(i)
    return l__Int
endfunction

private function Typecast2 takes nothing returns nothing
    local integer Str //Jasshelper will implicitly rename this to l__Str
    local string Int   //Jasshelper will implicitly rename this to l__Int
endfunction

//# +nosemanticerror
function SH2I takes string s returns integer
    call setStr(s)
    return l__Str
endfunction

//# +nosemanticerror
function I2SH takes integer i returns string
    call setInt(i)
    return l__Int
endfunction

private function Typecast3 takes nothing returns nothing
    local integer Bool //Jasshelper will implicitly rename this to l__Bool
    local boolean Int  //Jasshelper will implicitly rename this to l__Int
endfunction

//# +nosemanticerror
function B2I takes boolean b returns integer
    call setBool(b)
    return l__Bool
endfunction

//# +nosemanticerror
function I2B takes integer i returns boolean
    call setInt(i)
    return l__Int
endfunction

//# +nosemanticerror
function realToIndex takes real r returns integer
    return r
endfunction

function cleanInt takes integer i returns integer
    return i
endfunction

//# +nosemanticerror
function indexToReal takes integer i returns real
    return i
endfunction

function cleanReal takes real r returns real
    return r
endfunction

endlibrary

library DoesTypecastingWorkInNewestPatch initializer init requires Typecast

function do_nothing takes nothing returns nothing
    call BJDebugMsg("except printing this")
endfunction

private function init takes nothing returns nothing
    local integer fp_do_nothing = C2I(function do_nothing)
    local code do_nothing_func = I2C(fp_do_nothing)
    local boolean b = true
    local integer one = B2I(b)

    call ForForce(bj_FORCE_PLAYER[15], do_nothing_func)
    call BJDebugMsg("one: " + I2S(one))
    call BJDebugMsg("foo has string-table index: " + I2S(SH2I("foo")))
endfunction

endlibrary
 
Level 13
Joined
Nov 7, 2014
Messages
571
mind share newest code? dont wanna search my editor :X

Well, I use leandrotp's way of reading memory, I think you are doing something a bit different (not just because of having write access as well) in mem hack:

JASS:
// Accessing memory from the script: http://www.hiveworkshop.com/threads/accessing-memory-from-the-script-its-time-of-the-revolution.279262/
// Memory hack: http://www.hiveworkshop.com/threads/memory-hack.289508/

library mouse requires Version

globals
    public integer screen_x = 0
    public integer screen_y = 0
endglobals

public function get_screen_xy takes nothing returns nothing
    local integer i
    set i = Memory[ Memory[ Memory[ Memory[ GameBase + 0x00BBA0D0/4 ]/4 + 0x148/4 ]/4 + 0x90/4 ]/4 + 0x718/4 ]
    set screen_x = i - i / 0x00010000 * 0x00010000
    set i = i / 0x00010000
    set screen_y = i
endfunction

endlibrary

library Typecast

globals
    code Code //This is not used, it's here just to fool Jasshelper
    code l__Code
    integer Int //This is not used, it's here just to fool Jasshelper
    integer l__Int
    string Str //This is not used, it's here just to fool Jasshelper
    string l__Str
    boolean Bool //This is not used, it's here just to fool Jasshelper
    boolean l__Bool
endglobals

function setCode takes code c returns nothing
    set l__Code = c
    return //Prevents Jasshelper from inlining this function
endfunction

function setInt takes integer i returns nothing
    set l__Int = i
    return //Prevents JassHelper from inlining this function
endfunction

function setStr takes string s returns nothing
    set l__Str = s
    return //Prevents JassHelper from inlining this function
endfunction

function setBool takes boolean b returns nothing
    set l__Bool = b
    return //Prevents Jasshelper from inlining this function
endfunction

private function Typecast1 takes nothing returns nothing
    local integer Code //Jasshelper will implicitly rename this to l__Code
    local code Int     //Jasshelper will implicitly rename this to l__Int
endfunction

//# +nosemanticerror
function C2I takes code c returns integer
    call setCode(c)
    return l__Code
endfunction

//# +nosemanticerror
function I2C takes integer i returns code
    call setInt(i)
    return l__Int
endfunction

private function Typecast2 takes nothing returns nothing
    local integer Str //Jasshelper will implicitly rename this to l__Str
    local string Int   //Jasshelper will implicitly rename this to l__Int
endfunction

//# +nosemanticerror
function SH2I takes string s returns integer
    call setStr(s)
    return l__Str
endfunction

//# +nosemanticerror
function I2SH takes integer i returns string
    call setInt(i)
    return l__Int
endfunction

private function Typecast3 takes nothing returns nothing
    local integer Bool //Jasshelper will implicitly rename this to l__Bool
    local boolean Int  //Jasshelper will implicitly rename this to l__Int
endfunction

//# +nosemanticerror
function B2I takes boolean b returns integer
    call setBool(b)
    return l__Bool
endfunction

//# +nosemanticerror
function I2B takes integer i returns boolean
    call setInt(i)
    return l__Int
endfunction

//# +nosemanticerror
function realToIndex takes real r returns integer
    return r
endfunction

function cleanInt takes integer i returns integer
    return i
endfunction

//# +nosemanticerror
function indexToReal takes integer i returns real
    return i
endfunction

function cleanReal takes real r returns real
    return r
endfunction

endlibrary


library Memory initializer Init requires Typecast

// Variables must be public so they have undecorated names
globals
    integer teamScore // Memory array
    integer index     // Desired address
    integer bestScore // Returned value
    integer teamCount // Used to crash the Jass VM. Never use this variable!
    trigger MemReader = CreateTrigger()
endglobals

/* This code will change in the future, but the syntax will be the same
   Memory[i] returns the contents of memory at the address (i*4).
   Always pass the address you want to read divided by 4 !!! */

struct Memory
    static method operator [] takes integer address returns integer
        set index = address
        call TriggerEvaluate(MemReader)
        return bestScore
    endmethod

    static method operator []= takes integer address, integer value returns nothing
        return //not implemented
    endmethod
endstruct

private function Nothing takes nothing returns nothing
    return
endfunction

// Functions that take arguments normally can't be used as code.
//# +nosemanticerror
private function Init takes nothing returns nothing
    set teamScore = C2I(function Nothing)-8
    call TriggerAddCondition(MemReader, Condition(I2C(1648+C2I(function MeleeTournamentFinishNowRuleA))))
endfunction

endlibrary


/* Provides detection of the game version and initializes all
   version-specific addresses. Currently supported versions
   are 1.26 and 1.27, windows only. My goal is to support all
   versions since 1.24b at windows, and 1.27a on Mac. */

library Version initializer Init requires Memory

globals
    integer A
    integer array l__A
    integer GameBase
    integer GameState
    integer pUnitData
    integer pAbilityData

    public integer game_version
endglobals

private function ImLazy takes nothing returns nothing
    set l__A[1] = 1
endfunction

private function Typecast takes nothing returns nothing
    local integer A
endfunction

//# +nosemanticerror
private function A2I takes nothing returns integer
    return l__A
    return 0
endfunction

function Init26 takes nothing returns nothing
    set GameBase = Memory[A2I()/4]/4-0x254418
    set GameState = GameBase+0x2AD97D
    set pUnitData = GameBase+0x2AD11E
    set pAbilityData = GameBase+0x2ACF99

    set game_version = 26
endfunction

function Init27 takes nothing returns nothing
    set GameBase = Memory[A2I()/4]/4-0x298ECC
    set GameState = GameBase+0x2F908E
    set pUnitData = GameBase+0x2FB123
    set pAbilityData = GameBase+0x2FB351

    set game_version = 27
endfunction

private function Init takes nothing returns nothing
    local integer i
    call ImLazy()
    set i = Memory[A2I()/4]
    set i = i - Memory[i/4]
    if i == 2586768 then
        call Init27()
    elseif i == 5205600 then
        call Init26()
    else
        call BJDebugMsg("Unsupported version. All memory access is disabled")
        call DestroyTrigger(MemReader)
        set MemReader = null
    endif
endfunction

endlibrary


ghostly.j:
JASS:
library Ghostly requires mouse/*, Blackboard*/

globals
    // we use GetCameraBound* for these
    private real CAMERA_MIN_X
    private real CAMERA_MAX_X
    private real CAMERA_MIN_Y
    private real CAMERA_MAX_Y
    private constant boolean CAN_PASS_THROUGH_TERRAIN = false
    // The camera gets stuck around this height (camera z bound).
    private constant real CAMERA_MAX_Z = 4192.0

    private constant real DT = 1.0 / 32.0
endglobals

private function dd takes string s returns nothing
    call BJDebugMsg(s)
endfunction

private function b2s takes boolean b returns string
    if b then
        return "T"
    else
        return "F"
    endif
endfunction

// Without this function this library would suck!
// http://www.wc3c.net/showthread.php?p=1029058
// <3 Tc =)
private function SetCameraZ takes real z returns nothing
    set z = GetCameraField(CAMERA_FIELD_ZOFFSET) + z - GetCameraTargetPositionZ()
    call SetCameraField(CAMERA_FIELD_ZOFFSET, z, -0.01)
    call SetCameraField(CAMERA_FIELD_ZOFFSET, z, 0.01)
endfunction

globals
    private constant integer KEY_LEFT = 0
    private constant integer KEY_RIGHT = 1
    private constant integer KEY_DOWN = 2
    private constant integer KEY_UP = 3
    private boolean array is_player_key_down[/*player*/ 12][/*key*/ 4]

    private location loc = Location(0.0, 0.0)
endglobals

struct Ghostly extends array
    private boolean enabled
    private player pp

    real x
    real y
    real z
    real speed

    real rot
    real rot_inc
    integer rot_d

    real aoa
    real aoa_inc
    integer aoa_d

    real fov
    real farz

    // previous mouse screen position
    integer pmsx
    integer pmsy

    private static integer first_timer_offset
    timer t

    private static code on_cmd_handler
    private static code on_key_handler
    private static code update_handler
    private static method onInit takes nothing returns nothing
        local thistype this
        local trigger t
        local trigger t2

        call ExecuteFunc("s__" + "thistype" + "_set_handlers")

        set CAMERA_MIN_X = GetCameraBoundMinX()
        set CAMERA_MAX_X = GetCameraBoundMaxX()
        set CAMERA_MIN_Y = GetCameraBoundMinY()
        set CAMERA_MAX_Y = GetCameraBoundMaxY()

        set t = CreateTrigger()
        set t2 = CreateTrigger()
        set this = 0
        loop
            exitwhen this >= bj_MAX_PLAYERS
            set this.pp = Player(this)

            call TriggerRegisterPlayerChatEvent(t2, this.pp, "", false)

            // set this.enabled = false // default value

            // set this.x = 0.0
            set this.y = -530.0
            set this.z = 800.0
            set this.speed = 600.0

            set this.rot = 90.0
            set this.rot_inc = 3.0
            set this.rot_d = 3

            set this.aoa = -56.0
            set this.aoa_inc = 2.5
            set this.aoa_d = 2

            set this.fov = 120.0
            set this.farz = 3000.0

            // set this.pmsx = 0
            // set this.pmsy = 0

            call TriggerRegisterPlayerEvent(t, this.pp, EVENT_PLAYER_ARROW_LEFT_DOWN)
            call TriggerRegisterPlayerEvent(t, this.pp, EVENT_PLAYER_ARROW_LEFT_UP)
            call TriggerRegisterPlayerEvent(t, this.pp, EVENT_PLAYER_ARROW_RIGHT_DOWN)
            call TriggerRegisterPlayerEvent(t, this.pp, EVENT_PLAYER_ARROW_RIGHT_UP)
            call TriggerRegisterPlayerEvent(t, this.pp, EVENT_PLAYER_ARROW_DOWN_DOWN)
            call TriggerRegisterPlayerEvent(t, this.pp, EVENT_PLAYER_ARROW_DOWN_UP)
            call TriggerRegisterPlayerEvent(t, this.pp, EVENT_PLAYER_ARROW_UP_DOWN)
            call TriggerRegisterPlayerEvent(t, this.pp, EVENT_PLAYER_ARROW_UP_UP)

            set this = this + 1
        endloop

        set this = 0
        loop
            exitwhen this >= bj_MAX_PLAYERS
            set this.t = CreateTimer()
            set this = this + 1
        endloop
        set first_timer_offset = GetHandleId(thistype(0).t)

        call TriggerAddAction(t, on_key_handler)
        call TriggerAddAction(t2, on_cmd_handler)
    endmethod

    method enable takes boolean flag returns nothing
        if flag then
            set this.enabled = true
            call TimerStart(this.t, DT, true, update_handler)
        else
            set this.enabled = false
            call PauseTimer(this.t)

            if this.pp == GetLocalPlayer() then
                call ResetToGameCamera(0.0)
            endif
        endif
    endmethod

    static method on_cmd takes nothing returns nothing
        local string cmd
        local thistype this

        call ExecuteFunc("s__" + "thistype" + "_tokenize")

        set this = GetPlayerId(GetTriggerPlayer())
        set cmd = T[1]

        if cmd == "-gh-enable" then
            if T[2] == "1" or T[2] == "true" or T[2] == "t" then
                call this.enable(true)
            elseif T[2] == "0" or T[2] == "false" or T[2] == "f" then
                call this.enable(false)
            endif
        elseif cmd == "-gh-speed" then
            set this.speed = S2R(T[2])
        elseif cmd == "-gh-rot-inc" then
            set this.rot_inc = S2R(T[2])
        elseif cmd == "-gh-rot-d" then
            set this.rot_inc = S2I(T[2])
        elseif cmd == "-gh-aoa-inc" then
            set this.aoa_inc = S2R(T[2])
        elseif cmd == "-gh-aoa-d" then
            set this.aoa_inc = S2I(T[2])
        elseif cmd == "-gh-fov" then
            set this.fov = S2R(T[2])
        elseif cmd == "-gh-farz" then
            set this.farz = S2R(T[2])
        endif
    endmethod

    static method on_key takes nothing returns nothing
        local thistype this
        local integer p
        local integer e
        local integer key
        local boolean is_key_down
        local real ang
        local real ang2
        local real terrain_z

        set this = GetPlayerId(GetTriggerPlayer())
        if not this.enabled then
            return
        endif

        set p = this

        // EVENT_PLAYER_ARROW_LEFT_DOWN = ConvertPlayerEvent(261) // 0
        // EVENT_PLAYER_ARROW_LEFT_UP = ConvertPlayerEvent(262) // 1
        // EVENT_PLAYER_ARROW_RIGHT_DOWN = ConvertPlayerEvent(263) // 2
        // EVENT_PLAYER_ARROW_RIGHT_UP = ConvertPlayerEvent(264) // 3
        // EVENT_PLAYER_ARROW_DOWN_DOWN = ConvertPlayerEvent(265) // 4
        // EVENT_PLAYER_ARROW_DOWN_UP = ConvertPlayerEvent(266) // 5
        // EVENT_PLAYER_ARROW_UP_DOWN = ConvertPlayerEvent(267) // 6
        // EVENT_PLAYER_ARROW_UP_UP = ConvertPlayerEvent(268) // 7
        //
        set e = GetHandleId(GetTriggerEventId()) - 261 // 0 .. 7
        set key = e / 2 // 0 .. 3
        set is_key_down = e == 2 * key
        set is_player_key_down[key] = is_key_down

        //! runtextmacro GHOSTLY_CAMERA_UPDATE()
    endmethod

    static method update takes nothing returns nothing
        local thistype this = GetHandleId(GetExpiredTimer()) - first_timer_offset
        local integer p = this
        // local Blackboard bb = Blackboard(this)
        local integer dx
        local integer dy
        local real ang
        local real ang2
        local real terrain_z

        call mouse_get_screen_xy()
        set dx = this.pmsx - mouse_screen_x
        set dy = this.pmsy - mouse_screen_y

        // call bb.println("msx: " + I2S(mouse_screen_x))
        // call bb.println("msy: " + I2S(mouse_screen_y))
        // call bb.println("dx: " + R2S(dx))
        // call bb.println("dy: " + R2S(dy))

        // call bb.println("rot: " + R2S(this.rot))
        // call bb.println("aoa: " + R2S(this.aoa))

        // call bb.println("left down: " + b2s(is_player_key_down[KEY_LEFT]))
        // call bb.println("right down: " + b2s(is_player_key_down[KEY_RIGHT]))
        // call bb.println("down down: " + b2s(is_player_key_down[KEY_DOWN]))
        // call bb.println("up down: " + b2s(is_player_key_down[KEY_UP]))
        // call bb.flush(bb.HALF_WIDTH).show()

        if dx < -rot_d then
            set this.rot = this.rot - this.rot_inc
        elseif dx > rot_d then
            set this.rot = this.rot + this.rot_inc
        endif

        if dy < -aoa_d then
            set this.aoa = this.aoa - this.aoa_inc
        elseif dy > aoa_d then
            set this.aoa = this.aoa + this.aoa_inc
        endif

        if this.aoa < -89.0 then
            set this.aoa = -89.0
        elseif this.aoa > 89.0 then
            set this.aoa = 89.0
        endif

//! textmacro GHOSTLY_CAMERA_UPDATE
        if is_player_key_down[KEY_LEFT] then
            set ang = bj_DEGTORAD * (this.rot + 90.0)
            set this.x = this.x + this.speed * DT * Cos(ang)
            set this.y = this.y + this.speed * DT * Sin(ang)

        elseif is_player_key_down[KEY_RIGHT] then
            set ang = (this.rot - 90.0) * bj_DEGTORAD
            set this.x = this.x + this.speed * DT * Cos(ang)
            set this.y = this.y + this.speed * DT * Sin(ang)

        endif

        if is_player_key_down[KEY_DOWN] then
            set ang = bj_DEGTORAD * this.rot
            set ang2 = bj_DEGTORAD * this.aoa
            set this.x = this.x - Cos(ang) * Cos(ang2) * this.speed * DT
            set this.y = this.y - Sin(ang) * Cos(ang2) * this.speed * DT
            set this.z = this.z - Sin(ang2) * this.speed * DT

        elseif is_player_key_down[KEY_UP] then
            set ang = bj_DEGTORAD * this.rot
            set ang2 = bj_DEGTORAD * this.aoa
            set this.x = this.x + Cos(ang) * Cos(ang2) * this.speed * DT
            set this.y = this.y + Sin(ang) * Cos(ang2) * this.speed * DT
            set this.z = this.z + Sin(ang2) * this.speed * DT

        endif

        if this.x < CAMERA_MIN_X then
            set this.x = CAMERA_MIN_X
        elseif this.x > CAMERA_MAX_X then
            set this.x = CAMERA_MAX_X
        endif

        if this.y < CAMERA_MIN_Y then
            set this.y = CAMERA_MIN_Y
        elseif this.y > CAMERA_MAX_Y then
            set this.y = CAMERA_MAX_Y
        endif

static if not CAN_PASS_THROUGH_TERRAIN then
        call MoveLocation(loc, x, y)
        set terrain_z = GetLocationZ(loc)
        if this.z < terrain_z + 128.0 then
            set this.z = terrain_z + 128.0
        endif
endif
        if this.z > CAMERA_MAX_Z then
            set this.z = CAMERA_MAX_Z
        endif

        if this.pp == GetLocalPlayer() then
            call PanCameraToTimed(this.x, this.y, 0.0)
            call SetCameraZ(this.z)
             // disables the camera fidgeting (pressing the arrow keys moves the "normal" game camera as well)
            call PanCameraToTimed(0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF)
            call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE, 0.0, 0.0)

            call SetCameraField(CAMERA_FIELD_ROTATION, this.rot, 0.0)
            call SetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK, this.aoa, 0.0)

            call SetCameraField(CAMERA_FIELD_FIELD_OF_VIEW, this.fov, 0.0)
            call SetCameraField(CAMERA_FIELD_FARZ, this.farz, 0.0)
        endif
//! endtextmacro
        //! runtextmacro GHOSTLY_CAMERA_UPDATE()

        set this.pmsx = mouse_screen_x
        set this.pmsy = mouse_screen_y
    endmethod

    private static string array T
    private static integer T_count = 0
    private static method tokenize takes nothing returns nothing
        local string s = GetEventPlayerChatString()
        local integer si
        local integer i
        local string ch

        set T_count = 0
        set i = 0
        loop
            loop
                set ch = SubString(s, i, i + 1)
                exitwhen ch != " "
                set i = i + 1
            endloop

            exitwhen ch == ""
            set si = i

            loop
                set i = i + 1
                set ch = SubString(s, i, i + 1)
                exitwhen ch == " " or ch == ""
            endloop

            set T_count = T_count + 1
            set T[T_count] = SubString(s, si, i)
        endloop
    endmethod

    static method set_handlers takes nothing returns nothing
        set on_cmd_handler = function thistype.on_cmd
        set on_key_handler = function thistype.on_key
        set update_handler = function thistype.update
    endmethod
endstruct

endlibrary

Like I said, mouse_get_screen_xy, reads a single integer which has the y coordinate in the upper 16 bits and the x in the lower 16 bits.
 
Top